Dapp開發教程三 Asch Dapp Mini DAO

前邊兩篇教程能夠稱之爲熱身,從這裏開始,進入正題。 這一次,咱們要正式建立新的交易類型或者智能合約了。前端

1 建立合約sql

首先要進入dapp所在目錄數據庫

cd dapps/<dapp id>/

而後執行asch-cli的contract子命令json

asch-cli contract -a

接下來會提示輸入合約的名字,這裏輸入的是"Project"api

? Contract file name (without .js) Project
New contract created: ./contracts/Project.js
Updating contracts list
Done

這個命令會幫咱們作三件事數組

新增了合約模板文件modules/contracts/Project.js
在modules/helper/transaction-types.js註冊了交易類型
在modules.full.json中註冊了新的模塊
2 定義實體字段app

在實現一個智能合約以前,須要定義好合約執行後生成的交易數據實體,即最終存儲到區塊鏈上的是哪些數據,也就是至關於建立關係數據的表格 一個合約類型對應一張表格 表格的schema在blockchain.json中進行配置less

project類型比較簡單,只包含name和description字段 另外transactionId字段是每一個實體表格都須要的,是做爲基礎交易transactions的外鍵。異步

{
        "table": "asset_project",
        "alias": "t_p",
        "type": "table",
        "tableFields": [
            {
                "name": "name",
                "type": "String",
                "length": 16,
                "not_null": true
            },
            {
                "name": "description",
                "type": "Text",
                "not_null": true
            },
            {
                "name": "transactionId",
                "type": "String",
                "length": 21,
                "not_null": true
            }
        ],
        "foreignKeys": [
            {
                "field": "transactionId",
                "table": "transactions",
                "table_field": "id",
                "on_delete": "cascade"
            }
        ]
    }

而後須要在join字段種加入新的配置,仍是爲了聯合查詢以及序列化和反序列化時使用區塊鏈

{
                "type": "left outer",
                "table": "asset_project",
                "alias": "t_p",
                "on": {
                    "t.id": "t_p.transactionId"
                }
            }

未來,asch會把這些配置經過自動化的方式生成,開發者只須要輸入實體字段的名稱和類型便可。

3 實現合約接口

一個合約包含以下接口,有的必需要實現,有個則使用默認生成的代碼便可

create              # 建立一個交易的數據對象,主要是賦值操做
calculateFee        # 設置交易費,即生成一次交易須要消耗的XAS數量
verify              # 驗證交易數據,好比字段是否合法,依賴條件是否知足等
getBytes            # 返回交易的二進制數據,類型爲Buffer
apply               # 合約的執行邏輯,在區塊打包時調用,主要是分配和轉移交易涉及到的各個帳戶的資產,以及帳戶其餘字段的設置等
undo                # apply的相反操做,在區塊回滾時會調用
applyUnconfirmed    # 合約的預執行邏輯,與apply相似,可是這個會實時的調用,就是說區塊打包前就會調用,所以涉及到的帳戶操做都是臨時、未確認的
undoUnconfirmed     # applyUnconfirmed的相反操做,回滾時使用
ready               # 交易是否準備完畢,是否知足打包的條件,這是個高級功能,大部分狀況都不須要,之後會單獨講解
save                # 交易數據的序列化操做,就是將json字段映射到數據庫表格字段
dbRead              # 交易的反序列化操做,將數據庫表格字段映射到json字段
normalize           # 交易數據的格式化,把不相關的對象字段刪除,相關的對象統一類型,通常狀況不須要

上面的接口大部分狀況下使用默認的就能夠了 開發者須要注意的主要是apply和applyUnconfirmed兩個接口,這是業務邏輯的主體部分。

4 實現Project合約

實現create

trs.recipientId = null;
  // 建立項目只須要發起者,不須要接收者,因此設爲null

    trs.amount = 0;
  // 也不須要金額,只須要手續費

    trs.asset.project = {
        name: data.name,
        description: data.description
    }
  // project對象的兩個數據字段

    return trs;

設置交易費

這個項目不但願與XAS對接,那麼就把交易費設置爲0就好了

Project.prototype.calculateFee = function (trs) {
    return 0;
}

數據檢驗

這個沒啥可解釋的

Project.prototype.verify = function (trs, sender, cb, scope) {
    if (trs.recipientId) {
        return cb("Recipient should not exist");
    }
    if (trs.amount != 0) {
        return cb("Amount should be zero");
    }
    if (!trs.asset.project.name) {
        return cb("Project must have a name");
    }
    if (trs.asset.project.name.length > 16) {
        return cb("Project name must be 16 characters or less");
    }
    if (!trs.asset.project.description) {
        return cb("Invalid project description");
    }
    if (trs.asset.project.description.length > 1024) {
        return cb("Project description must be 1024 characters or less");
    }
    cb(null, trs);
}

獲取二進制數據

二進制數據主要是爲了生成簽名數據,因此只須要把交易的實體數據組合起來打包成Buffer就能夠了。 組合的方式能夠隨便,好比,能夠經過bytebuffer,也能夠經過簡單的字符串鏈接。

Project.prototype.getBytes = function (trs) {
    try {
        var buf = new Buffer(trs.asset.project.name + trs.asset.project.description, "utf8");
    } catch (e) {
        throw Error(e.toString());
    }

    return buf;
}

合約執行邏輯

先看未確認合約的執行

Project.prototype.applyUnconfirmed = function (trs, sender, cb, scope) {
    if (sender.u_balance["POINTS"] < BURN_POINTS) {
        return setImmediate(cb, "Account does not have enough POINTS: " + trs.id);
    }
    if (private.uProjects[trs.asset.project.name]){
        return setImmediate(cb, "Project already exists");
    }
    modules.blockchain.accounts.mergeAccountAndGet({
        address: sender.address,
        u_balance: { "POINTS": -BURN_POINTS }
    }, function (err, accounts) {
        if (!err) {
            private.uProjects[trs.asset.project.name] = trs;
        }
        cb(err, accounts);
    }, scope);
}

在這一步,檢查用戶的餘額是否足夠,不然拒絕執行, 接着判斷是否已經存在相同的項目名稱, 最後會看到一個dapp開發中最重要的api,即modules.blockchain.accounts.mergeAccountAndGet。

這個api的功能是對帳戶進行操做,這個操做包括對數字的加減法、數組的增刪、字符串的設置等。 這裏對帳戶餘額執行了減法操做,即把u_balance中的POINTS資產,減去BURN_POINTS。 這裏取名BURN_POINTS主要是爲了表達這個合約的執行須要燃燒必定數量的資產,由於沒有指定被消耗掉的資產的去向,那麼這些被消耗的資產就只有消失了,也就是被燃燒了。 這裏只是爲了簡單起見,若是業務邏輯不但願燃燒,能夠把這些資產做爲手續費,轉給應用的開發者或者節點運營者,或者轉移到一個基金帳戶中,用做未來的開發經費,徹底由你本身決定。

接下來再看看確認合約的執行代碼

Project.prototype.apply = function (trs, sender, cb, scope) {
    modules.blockchain.accounts.mergeAccountAndGet({
        address: sender.address,
        balance: {"POINTS": -BURN_POINTS}
    }, cb, scope);
}

很是簡單,只有一個操做,僅僅是對帳戶資產進行一個減法操做。 大部分狀況下, applyUnconfirmed是比apply要複雜的,特別是涉及到資產的減法操做時,由於前者要比後者執行的更早,後者就不必作多餘的條件檢查了。 咱們要注意到,apply修改的是balance字段,applyUnconfirmed修改的是u_balance字段,

因此若是u_balance知足條件(即有足夠的剩餘資產),那麼balance必定也會知足條件,因此就不必進行進一步檢查了。

接下來的save, dbRead就不必解釋了,開發者能夠本身發現其中的規律,直接套用便可。

5 實現http接口

在上一個步驟,已經定義了一個project合約的全部邏輯了。 在這一步,咱們須要增長兩個接口,都是爲客戶端或前端服務的,一個是用於建立交易,一個是用於查詢交易歷史。

幾乎全部的交易建立都是相似的,通常能夠分解成一下幾步

使用客戶端傳過來的secret生成密鑰對keypair
使用公鑰查詢或新建帳戶數據,經過api modules.blockchain.accounts.getAccount
而後使用客戶端傳過來的交易實體數據和帳戶數據以及密鑰對,建立一個交易對象,經過api modules.logic.transaction.create
最後是調用api modules.blockchain.transactions.processUnconfirmedTransaction來處理這個交易
有一點須要注意的是library.sequence.add接口的使用,這個接口能夠保證多個交易按前後順序嚴格執行,若是你的合約邏輯中涉及到異步操做,應該要使用這個api。

再來看一下list這個查詢接口,熟悉sql的同窗一眼就看出,這只不過是個聯表查詢操做。

爲何要聯表查詢呢?

由於transactions和asset_xxx表示的是一個交易的不一樣部分,前者是數據的基礎數據,全部交易都通用,好比交易的發起者,交易數據的簽名,金額等等, 後者則屬於交易數據的擴展部分,是用戶自定義的數據,與具體的業務邏輯相關。

6 實現投票合約

這個就不逐行解釋了,開發者能夠本身研究asch-mini-dao的源碼,有了上面的基礎後,不難理解。

相關文章
相關標籤/搜索