摘要:前邊兩篇教程可以稱(chēng)之為熱身,從這里開(kāi)始,進(jìn)入正題。這一次,我們要正式創(chuàng)建新的交易類(lèi)型或者智能合約了。這個(gè)的功能是對(duì)賬戶(hù)進(jìn)行操作,這個(gè)操作包括對(duì)數(shù)字的加減法數(shù)組的增刪字符串的設(shè)置等。
前邊兩篇教程可以稱(chēng)之為熱身,從這里開(kāi)始,進(jìn)入正題。 這一次,我們要正式創(chuàng)建新的交易類(lèi)型或者智能合約了。
1 創(chuàng)建合約
首先要進(jìn)入dapp所在目錄
cd dapps//
然后執(zhí)行asch-cli的contract子命令
asch-cli contract -a
接下來(lái)會(huì)提示輸入合約的名字,這里輸入的是"Project"
? Contract file name (without .js) Project New contract created: ./contracts/Project.js Updating contracts list Done
這個(gè)命令會(huì)幫我們做三件事
新增了合約模板文件modules/contracts/Project.js
在modules/helper/transaction-types.js注冊(cè)了交易類(lèi)型
在modules.full.json中注冊(cè)了新的模塊
2 定義實(shí)體字段
在實(shí)現(xiàn)一個(gè)智能合約之前,需要定義好合約執(zhí)行后生成的交易數(shù)據(jù)實(shí)體,即最終存儲(chǔ)到區(qū)塊鏈上的是哪些數(shù)據(jù),也就是相當(dāng)于創(chuàng)建關(guān)系數(shù)據(jù)的表格 一個(gè)合約類(lèi)型對(duì)應(yīng)一張表格 表格的schema在blockchain.json中進(jìn)行配置
project類(lèi)型比較簡(jiǎn)單,只包含name和description字段 另外transactionId字段是每個(gè)實(shí)體表格都需要的,是作為基礎(chǔ)交易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字段種加入新的配置,還是為了聯(lián)合查詢(xún)以及序列化和反序列化時(shí)使用
{ "type": "left outer", "table": "asset_project", "alias": "t_p", "on": { "t.id": "t_p.transactionId" } }
將來(lái),asch會(huì)把這些配置通過(guò)自動(dòng)化的方式生成,開(kāi)發(fā)者只需要輸入實(shí)體字段的名稱(chēng)和類(lèi)型即可。
3 實(shí)現(xiàn)合約接口
一個(gè)合約包含如下接口,有的必須要實(shí)現(xiàn),有個(gè)則使用默認(rèn)生成的代碼即可
create # 創(chuàng)建一個(gè)交易的數(shù)據(jù)對(duì)象,主要是賦值操作 calculateFee # 設(shè)置交易費(fèi),即生成一次交易需要消耗的XAS數(shù)量 verify # 驗(yàn)證交易數(shù)據(jù),比如字段是否合法,依賴(lài)條件是否滿(mǎn)足等 getBytes # 返回交易的二進(jìn)制數(shù)據(jù),類(lèi)型為Buffer apply # 合約的執(zhí)行邏輯,在區(qū)塊打包時(shí)調(diào)用,主要是分配和轉(zhuǎn)移交易涉及到的各個(gè)賬戶(hù)的資產(chǎn),以及賬戶(hù)其他字段的設(shè)置等 undo # apply的相反操作,在區(qū)塊回滾時(shí)會(huì)調(diào)用 applyUnconfirmed # 合約的預(yù)執(zhí)行邏輯,與apply類(lèi)似,但是這個(gè)會(huì)實(shí)時(shí)的調(diào)用,就是說(shuō)區(qū)塊打包前就會(huì)調(diào)用,因此涉及到的賬戶(hù)操作都是臨時(shí)、未確認(rèn)的 undoUnconfirmed # applyUnconfirmed的相反操作,回滾時(shí)使用 ready # 交易是否準(zhǔn)備完畢,是否滿(mǎn)足打包的條件,這是個(gè)高級(jí)功能,大部分情況都不需要,以后會(huì)多帶帶講解 save # 交易數(shù)據(jù)的序列化操作,就是將json字段映射到數(shù)據(jù)庫(kù)表格字段 dbRead # 交易的反序列化操作,將數(shù)據(jù)庫(kù)表格字段映射到j(luò)son字段 normalize # 交易數(shù)據(jù)的格式化,把不相關(guān)的對(duì)象字段刪除,相關(guān)的對(duì)象統(tǒng)一類(lèi)型,一般情況不需要
上面的接口大部分情況下使用默認(rèn)的就可以了 開(kāi)發(fā)者需要注意的主要是apply和applyUnconfirmed兩個(gè)接口,這是業(yè)務(wù)邏輯的主體部分。
4 實(shí)現(xiàn)Project合約
實(shí)現(xiàn)create
trs.recipientId = null; // 創(chuàng)建項(xiàng)目只需要發(fā)起者,不需要接收者,所以設(shè)為null trs.amount = 0; // 也不需要金額,只需要手續(xù)費(fèi) trs.asset.project = { name: data.name, description: data.description } // project對(duì)象的兩個(gè)數(shù)據(jù)字段 return trs;
設(shè)置交易費(fèi)
這個(gè)項(xiàng)目不希望與XAS對(duì)接,那么就把交易費(fèi)設(shè)置為0就行了
Project.prototype.calculateFee = function (trs) { return 0; }
數(shù)據(jù)檢驗(yàn)
這個(gè)沒(méi)啥可解釋的
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); }
獲取二進(jìn)制數(shù)據(jù)
二進(jìn)制數(shù)據(jù)主要是為了生成簽名數(shù)據(jù),所以只需要把交易的實(shí)體數(shù)據(jù)組合起來(lái)打包成Buffer就可以了。 組合的方式可以隨便,比如,可以通過(guò)bytebuffer,也可以通過(guò)簡(jiǎn)單的字符串連接。
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; }
合約執(zhí)行邏輯
先看未確認(rèn)合約的執(zhí)行
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); }
在這一步,檢查用戶(hù)的余額是否足夠,否則拒絕執(zhí)行, 接著判斷是否已經(jīng)存在相同的項(xiàng)目名稱(chēng), 最后會(huì)看到一個(gè)dapp開(kāi)發(fā)中最重要的api,即modules.blockchain.accounts.mergeAccountAndGet。
這個(gè)api的功能是對(duì)賬戶(hù)進(jìn)行操作,這個(gè)操作包括對(duì)數(shù)字的加減法、數(shù)組的增刪、字符串的設(shè)置等。 這里對(duì)賬戶(hù)余額執(zhí)行了減法操作,即把u_balance中的POINTS資產(chǎn),減去BURN_POINTS。 這里取名BURN_POINTS主要是為了表達(dá)這個(gè)合約的執(zhí)行需要燃燒一定數(shù)量的資產(chǎn),因?yàn)闆](méi)有指定被消耗掉的資產(chǎn)的去向,那么這些被消耗的資產(chǎn)就只有消失了,也就是被燃燒了。 這里只是為了簡(jiǎn)單起見(jiàn),如果業(yè)務(wù)邏輯不希望燃燒,可以把這些資產(chǎn)作為手續(xù)費(fèi),轉(zhuǎn)給應(yīng)用的開(kāi)發(fā)者或者節(jié)點(diǎn)運(yùn)營(yíng)者,或者轉(zhuǎn)移到一個(gè)基金賬戶(hù)中,用作將來(lái)的開(kāi)發(fā)經(jīng)費(fèi),完全由你自己決定。
接下來(lái)再看看確認(rèn)合約的執(zhí)行代碼
Project.prototype.apply = function (trs, sender, cb, scope) { modules.blockchain.accounts.mergeAccountAndGet({ address: sender.address, balance: {"POINTS": -BURN_POINTS} }, cb, scope); }
非常簡(jiǎn)單,只有一個(gè)操作,僅僅是對(duì)賬戶(hù)資產(chǎn)進(jìn)行一個(gè)減法操作。 大部分情況下, applyUnconfirmed是比apply要復(fù)雜的,特別是涉及到資產(chǎn)的減法操作時(shí),因?yàn)榍罢咭群笳邎?zhí)行的更早,后者就沒(méi)必要做多余的條件檢查了。 我們要注意到,apply修改的是balance字段,applyUnconfirmed修改的是u_balance字段,
所以如果u_balance滿(mǎn)足條件(即有足夠的剩余資產(chǎn)),那么balance一定也會(huì)滿(mǎn)足條件,所以就沒(méi)必要進(jìn)行進(jìn)一步檢查了。
接下來(lái)的save, dbRead就沒(méi)必要解釋了,開(kāi)發(fā)者可以自己發(fā)現(xiàn)其中的規(guī)律,直接套用即可。
5 實(shí)現(xiàn)http接口
在上一個(gè)步驟,已經(jīng)定義了一個(gè)project合約的所有邏輯了。 在這一步,我們需要增加兩個(gè)接口,都是為客戶(hù)端或前端服務(wù)的,一個(gè)是用于創(chuàng)建交易,一個(gè)是用于查詢(xún)交易歷史。
幾乎所有的交易創(chuàng)建都是類(lèi)似的,一般可以分解成一下幾步
使用客戶(hù)端傳過(guò)來(lái)的secret生成密鑰對(duì)keypair
使用公鑰查詢(xún)或新建賬戶(hù)數(shù)據(jù),通過(guò)api modules.blockchain.accounts.getAccount
然后使用客戶(hù)端傳過(guò)來(lái)的交易實(shí)體數(shù)據(jù)和賬戶(hù)數(shù)據(jù)以及密鑰對(duì),創(chuàng)建一個(gè)交易對(duì)象,通過(guò)api modules.logic.transaction.create
最后是調(diào)用api modules.blockchain.transactions.processUnconfirmedTransaction來(lái)處理這個(gè)交易
有一點(diǎn)需要注意的是library.sequence.add接口的使用,這個(gè)接口可以保證多個(gè)交易按先后順序嚴(yán)格執(zhí)行,如果你的合約邏輯中涉及到異步操作,應(yīng)該要使用這個(gè)api。
再來(lái)看一下list這個(gè)查詢(xún)接口,熟悉sql的同學(xué)一眼就看出,這只不過(guò)是個(gè)聯(lián)表查詢(xún)操作。
為什么要聯(lián)表查詢(xún)呢?
因?yàn)閠ransactions和asset_xxx表示的是一個(gè)交易的不同部分,前者是數(shù)據(jù)的基礎(chǔ)數(shù)據(jù),所有交易都通用,比如交易的發(fā)起者,交易數(shù)據(jù)的簽名,金額等等, 后者則屬于交易數(shù)據(jù)的擴(kuò)展部分,是用戶(hù)自定義的數(shù)據(jù),與具體的業(yè)務(wù)邏輯相關(guān)。
6 實(shí)現(xiàn)投票合約
這個(gè)就不逐行解釋了,開(kāi)發(fā)者可以自己研究asch-mini-dao的源碼,有了上面的基礎(chǔ)后,不難理解。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81333.html
摘要:前邊兩篇教程可以稱(chēng)之為熱身,從這里開(kāi)始,進(jìn)入正題。這一次,我們要正式創(chuàng)建新的交易類(lèi)型或者智能合約了。這個(gè)的功能是對(duì)賬戶(hù)進(jìn)行操作,這個(gè)操作包括對(duì)數(shù)字的加減法數(shù)組的增刪字符串的設(shè)置等。 前邊兩篇教程可以稱(chēng)之為熱身,從這里開(kāi)始,進(jìn)入正題。 這一次,我們要正式創(chuàng)建新的交易類(lèi)型或者智能合約了。 1 創(chuàng)建合約 首先要進(jìn)入dapp所在目錄 cd dapps// 然后執(zhí)行asch-cli的contr...
摘要:但是我覺(jué)得在原理上與上一個(gè)項(xiàng)目相比,并沒(méi)有什么不同。源碼是最好的老師。 這個(gè)dice game與上一個(gè)mini dao相比,代碼規(guī)模大了許多,功能也復(fù)雜了很多,創(chuàng)建了三個(gè)合約類(lèi)型,彼此之間有依賴(lài)關(guān)系,合約的執(zhí)行還要依賴(lài)歷史交易數(shù)據(jù)。 但是我覺(jué)得在原理上與上一個(gè)項(xiàng)目相比,并沒(méi)有什么不同。 源碼是最好的老師。
摘要:但是我覺(jué)得在原理上與上一個(gè)項(xiàng)目相比,并沒(méi)有什么不同。源碼是最好的老師。 這個(gè)dice game與上一個(gè)mini dao相比,代碼規(guī)模大了許多,功能也復(fù)雜了很多,創(chuàng)建了三個(gè)合約類(lèi)型,彼此之間有依賴(lài)關(guān)系,合約的執(zhí)行還要依賴(lài)歷史交易數(shù)據(jù)。 但是我覺(jué)得在原理上與上一個(gè)項(xiàng)目相比,并沒(méi)有什么不同。 源碼是最好的老師。
摘要:基本流程有三種,,,,后兩種是發(fā)布到線(xiàn)上的,可通過(guò)公網(wǎng)訪(fǎng)問(wèn)。第一種是運(yùn)行在本地的只有一個(gè)節(jié)點(diǎn)的私鏈,主要是為了方便本地測(cè)試和開(kāi)發(fā)。 1 基本流程 Asch有三種net,localnet,testnet,mainnet,后兩種是發(fā)布到線(xiàn)上的,可通過(guò)公網(wǎng)訪(fǎng)問(wèn)。 第一種localnet是運(yùn)行在本地的、只有一個(gè)節(jié)點(diǎn)的私鏈,主要是為了方便本地測(cè)試和開(kāi)發(fā)。 Dapp的開(kāi)發(fā)同樣要涉及到這三種網(wǎng)絡(luò),即...
摘要:前一篇文章介紹了開(kāi)發(fā)的基本流程,這一次打算創(chuàng)建一個(gè)擁有內(nèi)置資產(chǎn)的,并順便介紹下前后端通訊的協(xié)議和常用接口。我們的程序目前只能創(chuàng)建一種內(nèi)置資產(chǎn),如果有創(chuàng)建多種資產(chǎn)的需求,我們可以考慮開(kāi)發(fā)。 前一篇文章介紹了asch dapp開(kāi)發(fā)的基本流程,這一次打算創(chuàng)建一個(gè)擁有內(nèi)置資產(chǎn)的dapp,并順便介紹下前后端通訊的協(xié)議和常用接口。 1 創(chuàng)建一個(gè)帶內(nèi)置資產(chǎn)的dapp 其實(shí)這篇文章有些標(biāo)題黨,因?yàn)閯?chuàng)建...
閱讀 3871·2021-07-28 18:10
閱讀 2580·2019-08-30 15:44
閱讀 1087·2019-08-30 14:07
閱讀 3464·2019-08-29 17:20
閱讀 1580·2019-08-26 18:35
閱讀 3538·2019-08-26 13:42
閱讀 1820·2019-08-26 11:58
閱讀 1592·2019-08-23 18:33