BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

  百度超級鏈XuperChain是一個支持多語言合約的區塊鏈框架,有多種語言來供大家選擇使用開發智能合約。目前超級鏈的智能合約可以使用C++ 或者 Go 語言來編寫,理論上任何可以編譯成Wasm字節碼的語言都可以用來編寫超級鏈的智能合約。C++ 合約相對Go合約性能會更好些,go合約在易用性上更好,開發者可以根據需要選擇自己喜歡的語言來編寫智能合約,這篇文章會通過一步步的指引來幫助大家使用C++ 或者Go來編寫超級鏈的智能合約。下面的內容將按照步驟拆解,手把手教你如何順利完成智能合約的編寫,部署和測試,耐心讀完本文相信你會對超級鏈智能合約建立完整認知,快速get新技能。

  1. 準備工作

  1.1環境要求

  目前超級鏈節點主要運行在linux和mac上,windows不能運行超級鏈節點。

  1.go >= 1.12.x && <= 1.13.x

  2.g++ >= 4.8.2 或者 clang++ >= 3.3

  3.Docker

  1.2下載編譯XuperChain

  智能合約只有部署到鏈上才能運行,因此我們首先要編譯並啟動xuperchain節點。

  如果需要使用特定分支,使用git checkout來切換特定分支,如 git checkout v3.7

<code>$ cd $HOME
$ git clone https://github.com/xuperchain/xuperchain.git  xuperchain
$ cd xuperchain && make  /<code>

  1.3設置環境變量

  這些環境變量有助於我們更方便的執行一些命令而不用指定命令的全路徑。

<code>export PATH=$HOME/xuperchain/output:$PATH
export XDEV_ROOT=$HOME/xuperchain/core/contractsdk/cpp/<code>

  

1.4啟動XuperChain

  --vm ixvm參數是選擇ixvm合約虛擬機,開發合約過程中使用ixvm虛擬機能加快合約部署

<code>$ cd output
## 首先創建鏈
$ ./xchain-cli createChain
## 後臺啟動xuperchain節點
$ nohup ./xchain --vm ixvm &  /<code>

  1.5創建合約賬號

  合約賬號用來進行合約管理,比如合約的權限控制等,要部署合約必須創建合約賬號,同時合約賬號裡面需要有充足的xuper來部署合約。

  創建合約賬號 XC1111111111111111@xuper

<code>$ ./xchain-cli account new --account 1111111111111111 --fee 2000

contract response:
        {
            "pm": {
                "rule": 1,
                "acceptValue": 1.0
            },
            "aksWeight": {
                "dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN": 1.0
            }
        }

The gas you cousume is: 1000
The fee you pay is: 2000
Tx id: d62704970705a2682e2bd2c5b4f791065871fd45f64c87815b91d8a00039de35
account name: XC1111111111111111@xuper /<code>

  給合約賬號轉賬

  $ ./xchain-cli transfer --to XC1111111111111111@xuper --amount 100000000

  cd26657006f6f75f07bd53ad0a7fe74d76985cd592542d8cc87dc3fcdde115f5

  1.6小結

  至此我們完成了所有的準備工作,包括編譯XuperChain,創建鏈,啟動節點,創建合約賬號,後面我們開始體驗怎麼編譯,部署和調用智能合約。

  2.快速體驗

  在開始編寫智能合約之前首先通過一個簡單的例子來給大家演示合約是如何從代碼到字節碼,以及如何部署到鏈上,如何發起對智能合約的調用。我們使用一個c++合約為例來展示如何編譯、部署、調用合約。

  2.1創建合約工程

  xdev工具是隨XuperChain發佈的一個合約編譯和測試工具,在編譯完xuperchain之後生成在output目錄。

  xdev提供了一個默認的c++合約工程模板

<code>$ xdev init hello-cpp  /<code>

  這個命令創建了一個hello-cpp的合約工程

  2.2編譯合約

  第一次編譯的時間會長一點,因為xdev需要下載編譯器鏡像,以及編譯超級鏈的標準庫。

<code>$ xdev build -o hello.wasm
CC main.cc
LD wasm/<code>

  編譯結果為hello.wasm,後面我們使用這個文件來部署合約

  2.3部署合約

<code>$ ./xchain-cli wasm deploy --account XC1111111111111111@xuper --cname hello  --fee 5200000 --runtime c ./hello-cpp/hello.wasm

contract response: initialize succeed
The gas you cousume is: 151875
The fee you pay is: 5200000
Tx id: 8c33a91c5cf564a28e7b62cad827ba91e19abf961702659dd8b70a3fb872bdf1  /<code> 

  此命令看起來很長,但是其中很多參數都有默認值,我們先來看一下參數的含義:

  • wasm deploy :此為部署wasm合約的命令參數,不做過多解釋

  •--account XC1111111111111111@xuper:此為部署wasm合約的賬號(只有合約賬號才能進行合約的部署)

  •--cname hello:這裡的hello是指部署後在鏈上的合約名字,可以自行命名(但有規則,長度在4~16字符)

  •--runtime c 指明我們部署的是一個c++代碼編譯的合約,如果是go合約這裡填go即可。

  •--fee 為我們部署這個合約所需要的xuper

  •最後的hello.wasm是合約編譯好的文件

  2.4調用合約

<code>$ ./xchain-cli wasm invoke --method hello --fee 110000 hello

contract response: hello world
The gas you cousume is: 35
The fee you pay is: 110000
Tx id: d8989ad1bfd2d08bd233b7a09a544cb07976fdf3429144c42f6166d28e9ff695  /<code>

  參數解釋如下:

  •wasm invoke 表示我們要調用一個合約

  •--method hello 表示我們要調用合約的hello方法

  •--fee 指明我們這次調用合約花費的xuper

  •最後的參數指明我們調用的合約名字hello

  2.5小結

  通過本節的學習,我們快速掌握瞭如果編譯,部署和調用合約,在下面的章節裡面我們學些如果使用C++或者Go語言來編寫智能合約。

  3.合約編寫詳解

  XuperChain目前主要支持兩種編譯成wasm格式的合約語言,c++和go,合約框架的整體結構是一致的,在不同語言上的表現形式不太一樣,但熟悉一種語言的SDK之後很容易遷移到其他語言。

  下面大概說明如何編寫這兩種類型的合約

  3.1 C++合約

  以counter合約為例來看如何編寫一個C++合約。

  3.1.1合約樣例

  代碼在contractsdk/cpp/example/counter.cc

<code>#include "xchain/xchain.h"

struct Counter : public xchain::Contract {};

DEFINE_METHOD(Counter, initialize) {
    xchain::Context* ctx = self.context();
    const std::string& creator = ctx->arg("creator");
    if (creator.empty()) {
        ctx->error("missing creator");
        return;
    }
    ctx->put_object("creator", creator);
    ctx->ok("initialize succeed");
}

DEFINE_METHOD(Counter, increase) {
    xchain::Context* ctx = self.context();
    const std::string& key = ctx->arg("key");
    std::string value;
    ctx->get_object(key, &value);
    int cnt = 0;
    cnt = atoi(value.c_str());
    char buf[32];
    snprintf(buf, 32, "%d", cnt + 1);
    ctx->put_object(key, buf);
    ctx->ok(buf);
}

DEFINE_METHOD(Counter, get) {
    xchain::Context* ctx = self.context();
    const std::string& key = ctx->arg("key");
    std::string value;
    if (ctx->get_object(key, &value)) {
        ctx->ok(value);
    } else {
        ctx->error("key not found");
    }
}  /<code>

  

3.1.2代碼解析

  下面我們逐行解析合約代碼:

  •#include 為必須的,裡面包含了編寫合約所需要的庫。

  • struct Counter : public xchain::Contract {}; 聲明瞭我們的合約類,所有的合約類都要繼承自xchain::Contract。

  • DEFINE_METHOD(Counter, initialize) 我們通過DEFINE_METHOD來為合約類定義合約方法,在這個例子裡面我們為Counter類定義了一個叫initialize的合約方法。

  • xchain::Context* ctx = self.context();用來獲取合約的上下文,每個合約都有一個對應的合約執行上下文,通過上下文我們可以獲取合約參數,寫入合約數據,context對象是我們經常要操作的一個對象。

  • const std::string& creator = ctx->arg("creator");,用於從合約上下文裡面獲取合約方法的參數,這裡我們獲取了名字叫creator的合約參數,合約的參數列表是一個map結構, key為合約參數的名字,value為參數對應的用戶傳遞的值。

  • ctx->put_object("creator", creator); 通過合約上下文的put_object方法,我們可以向鏈上寫入數據。

  • ctx->ok("initialize succeed"); 用於返回合約的執行結果,如果合約執行失敗則調用ctx->error。

  通過上面的代碼分析我們得到了如下知識

  •一個合約有多個方法組成,如counter合約的initialize,increase, get方法。

  • initialize是每個合約必須實現的方法,這個合約方法會在部署合約的時候自動執行。

  •每個合約方法有一個Context對象,通過這個對象我們能獲取到很多有用的方法,如獲取用戶參數等。

  •通過Context對象的ok或者error方法我們能給調用方反饋合約的執行情況:成功或者失敗。

  更多的c++語言合約例子在超級鏈項目的core/contractsdk/cpp/example裡面尋找。

  3.2 Go合約

  以counter合約為例來看如何編寫一個go合約。

  3.2.1合約樣例

  代碼在contractsdk/go/example/counter/counter.go

<code>package main

import (
    "strconv"

    "github.com/xuperchain/xuperunion/contractsdk/go/code"
    "github.com/xuperchain/xuperunion/contractsdk/go/driver"
)

type counter struct{}

func (c *counter) Initialize(ctx code.Context) code.Response {
    creator, ok := ctx.Args()["creator"]
    if !ok {
        return code.Errors("missing creator")
    }
    err := ctx.PutObject([]byte("creator"), creator)
    if err != nil {
        return code.Error(err)
    }
    return code.OK(nil)
}

func (c *counter) Increase(ctx code.Context) code.Response {
    key, ok := ctx.Args()["key"]
    if !ok {
        return code.Errors("missing key")
    }
    value, err := ctx.GetObject(key)
    cnt := 0
    if err == nil {
        cnt, _ = strconv.Atoi(string(value))
    }

    cntstr := strconv.Itoa(cnt + 1)

    err = ctx.PutObject(key, []byte(cntstr))
    if err != nil {
        return code.Error(err)
    }
    return code.OK([]byte(cntstr))
}

func (c *counter) Get(ctx code.Context) code.Response {
    key, ok := ctx.Args()["key"]
    if !ok {
        return code.Errors("missing key")
    }
    value, err := ctx.GetObject(key)
    if err != nil {
        return code.Error(err)
    }
    return code.OK(value)
}

func main() {
    driver.Serve(new(counter))
}  /<code>

  go合約的整體框架結構跟c++合約一樣,在表現形式上稍微有點不一樣:

  •c++合約使用DEFINE_METHOD來定義合約方法,go通過結構體方法來定義合約方法

  •c++通過ctx->ok來返回合約數據,go通過返回code.Response對象來返回合約數據

  • go合約需要在main函數里面調用driver.Serve來啟動合約。

  更多的go語言合約例子在超級鏈項目的core/contractsdk/go/example裡面尋找。

  3.2.2合約編譯

  Go合約使用如下命令來編譯合約

<code>GOOS=js GOARCH=wasm go build -o hello.wasm  /<code>

  3.2.3合約部署

  Go合約部署唯一跟c++合約不一樣的地方在於--runtime參數,完整命令如下

<code>$ ./xchain-cli wasm deploy --account XC1111111111111111@xuper --cname hello  --fee 5200000 --runtime go ./hello-go/hello.wasm/<code>

  Go合約的調用跟c++合約參數一致。

  3.3小結

  在這個章節裡面我們學習瞭如何使用c++和go語言來編寫合約,更多的合約例子可以在對應語言SDK的example目錄裡面尋找,在下一章節我們學習如果給合約編寫單元測試。

  4.合約單測

  如果每次測試合約都需要部署到鏈上再發起調用會特別麻煩,xdev工具提供了單測能力,可以脫離鏈上環境運行合約。

  test目錄下放著合約測試文件,文件以 .test.js結尾,可以有多個測試文件。以hello-cpp目錄下的test/hello.test.js為例,文件內容如下:

<code>var assert = require("assert");

Test("hello", function (t) {
    var contract;
    t.Run("deploy", function (tt) {
        contract = xchain.Deploy({
            name: "hello",
            code: "../hello.wasm",
            lang: "c",
            init_args: {}
        })
    });

    t.Run("invoke", function (tt) {
        resp = contract.Invoke("hello", {});
        assert.equal(resp.Body, "hello world");
    })
})  /<code>

  使用Test函數來定義測試case,hello為測試名字, 匿名js function作為測試的body。全局對象xchain是我們跟xchain環境打交道的入口,xchain.Deploy用來部署一個合約到xchain環境,返回的contract對象,調用contract.Invoke方法即可調用合約。Deploy和Invoke方法都是通過拋出異常的方式來處理錯誤,測試框架會自動捕獲錯誤來結束測試case。t.Run可以定義子測試case。

  使用如下命令來啟動測試

<code>$ cd hello-cpp
$ xdev test # 測試test目錄下的所有case

=== RUN   hello
=== RUN   hello/deploy
=== RUN   hello/invoke
--- PASS: hello (0.11s)
    --- PASS: hello/deploy (0.07s)
    --- PASS: hello/invoke (0.02s)
PASS  /<code>

  5. VSCode編輯器集成

  配置編譯和測試task

  為了方便在vscode裡面編譯和測試合約,在.vscode/tasks.json裡面添加如下內容

<code>{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "xdev build",
            "type": "shell",
            "command": "xdev build -p",
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "xdev test",
            "type": "shell",
            "command": "xdev test",
            "options": {
                "cwd": "${workspaceFolder}"
            }
        }
    ]
} /<code>

  編譯合約

  Run Build Task(⇧⌘B)來啟動構建

BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

  跑合約單測

  調用Run Task命令之後,選擇xdev test來觸發單元測試

BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

代碼補全

  為了讓vscode幫我們自動補全代碼,需要做如下配置,在項目的.vscode/settings.json文件裡面加上這一個配置

<code>{
    "C_Cpp.default.compileCommands": "${workspaceFolder}/compile_commands.json"
}/<code>

  之後就能用vscode的自動補全功能了.

BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

  6.開放網絡集成環境

  超級鏈開放網絡是基於百度自研底層技術搭建的區塊鏈基礎服務網絡,符合中國標準,超級節點遍佈全國,區塊鏈網絡完全開放,為用戶提供區塊鏈快速部署和運行的環境,最低2元錢就用上的區塊鏈服務,讓信任鏈接更加便利。

  超級鏈開放網絡為開發者提供了合約開發、編譯、部署、管理的一站式可視化集成環境,下面介紹如何在開放網絡上開發部署智能合約。

BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

  6.1賬戶註冊

  1. 在超級鏈官網https://xchain.baidu.com/使用百度賬號登錄,如果沒有百度賬號請先註冊。

  2. 進入超級鏈開放網絡控制檯,第一次登錄的用戶,平臺會為用戶創建區塊鏈賬戶,請按照創建賬戶指引文檔完成安全碼設置,並記錄自己的助記詞和私鑰。

  3.

BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

  

6.2創建合約賬戶

  1.在工作臺,選擇「開放網絡 —> 合約管理」,點擊「創建合約賬戶」

  2.進入創建合約賬戶頁,輸入安全碼後點擊「確認創建」,系統自動生成賬戶名稱後,即創建完畢

  3.

BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

  6.3合約開發和部署

  1.在工作臺,選擇「開放網絡 —> 合約管理」,點擊「創建智能合約」

  2. 進入新頁面,按要求填寫基本信息、編輯合約代碼,編譯成功後點擊「安裝」,即可進入合約安裝(部署)流程。合約代碼編譯有兩種方式:

  •模板合約;選擇模板後,只需在模板代碼中填寫相關參數即可(參考模板詳情完成參數填寫)

  •自定義合約;在編輯器內完成C++語言的合約編輯即可

  •

BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

  3. 進入安裝流程,用戶需按合約代碼完成預執行操作。點擊「開始驗證」,執行通過會進入安裝確認頁

  •模板合約;系統會提供模板的函數,只需填寫參數即可(可參考模板詳情)

  •自定義合約;根據頁面操作說明,完成函數、參數填寫

  •

BSN智能合約開發培訓-百度超級鏈Xuperchain(四)

  4. 進入確認安裝頁,頁面顯示安裝合約預計消耗的餘額。點擊「安裝合約」將合約上鍊,上鍊過程需要等待10S左右。安裝完成後,在合約管理列表中可看到合約狀態變更為‘安裝成功’,即該合約已完成安裝。

  6.4合約調用

  目前開放網絡支持通過Go和Javascript兩種SDK調用智能合約。

  • Go SDK:

  https://github.com/xuperchain/xuper-java-sdk

  • Javascript SDK:

  https://github.com/xuperchain/xuper-sdk-js

  7.結語

  以上就是關於如何編寫超級鏈智能合約的錦囊秘籍,可以助你從小白秒變資深區塊鏈開發者。百度超級鏈正在聯合BSN舉辦區塊鏈創新開發者大賽,現在基於百度超級鏈編寫智能合約將有機會獲得萬元大獎,免費享受專業的區塊鏈開發培訓,點擊閱讀原文速去報名吧!


分享到:


相關文章: