儲蓄分成合約指的是項目方發起了一個鎖倉計劃(即儲蓄合約和取現合約),用戶能夠在準備期自由選擇鎖倉金額參與該計劃,等到鎖倉到期以後還能夠自動獲取鎖倉的利潤。用戶能夠在準備期內(dueBlockHeight
)參與儲蓄,按照合約規定能夠 1:1
獲取同等數量的儲蓄票據資產,同時用戶鎖倉的資產(deposit
)將放到取現合約中,而且項目方是沒法動用的,等到鎖倉期限(expireBlockHeight
)一到,用戶即可以調用取現合約將本身儲蓄的資產連本待息一同取出來。其示意圖以下:html
從上圖中能夠看出,項目方發佈了一個利潤爲20%
的鎖倉項目,其中儲蓄合約FixedLimitCollect
鎖定了1000
個票據資產(bill
),同時項目方將200
個儲蓄資產(deposit
)鎖定到利息合約中。待項目方發佈完合約以後,全部用戶即可以參與了。例如上圖中user1
調用合約儲蓄了500
,這500
個儲蓄資產將被鎖定在取現合約FixedLimitProfit
中,同時user1
得到了500
個票據資產,剩餘找零的資產將繼續鎖定在儲蓄合約FixedLimitCollect
中,以此類推,user2
和user3
也是相同的流程,直到儲蓄合約沒有資產爲止。取現合約FixedLimitProfit
跟儲蓄合約的模型大體相同,只是取現合約是由多個UTXO
組成的,用戶在取現的時候能夠並行操做。可是若是合約中的面值不能支持用戶一次性取現的話,須要分屢次提取。例如user1
擁有500
個票據資產,而能夠得到的本息總額爲600
,可是取現的UTXO
面值爲500
,那麼user1
一次最多隻能取500
,剩下的100
須要再構造一筆交易來提現。前端
// 儲蓄合約 import "./FixedLimitProfit" contract FixedLimitCollect(assetDeposited: Asset, totalAmountBill: Amount, totalAmountCapital: Amount, dueBlockHeight: Integer, expireBlockHeight: Integer, additionalBlockHeight: Integer, banker: Program, bankerKey: PublicKey) locks billAmount of billAsset { clause collect(amountDeposited: Amount, saver: Program) { verify below(dueBlockHeight) verify amountDeposited <= billAmount && totalAmountBill <= totalAmountCapital define sAmountDeposited: Integer = amountDeposited/100000000 define sTotalAmountBill: Integer = totalAmountBill/100000000 verify sAmountDeposited > 0 && sTotalAmountBill > 0 if amountDeposited < billAmount { lock amountDeposited of assetDeposited with FixedLimitProfit(billAsset, totalAmountBill, totalAmountCapital, expireBlockHeight, additionalBlockHeight, banker, bankerKey) lock amountDeposited of billAsset with saver lock billAmount-amountDeposited of billAsset with FixedLimitCollect(assetDeposited, totalAmountBill, totalAmountCapital, dueBlockHeight, expireBlockHeight, additionalBlockHeight, banker, bankerKey) } else { lock amountDeposited of assetDeposited with FixedLimitProfit(billAsset, totalAmountBill, totalAmountCapital, expireBlockHeight, additionalBlockHeight, banker, bankerKey) lock billAmount of billAsset with saver } } clause cancel(bankerSig: Signature) { verify above(dueBlockHeight) verify checkTxSig(bankerKey, bankerSig) unlock billAmount of billAsset } }
// 取現合約(本金加利息) contract FixedLimitProfit(assetBill: Asset, totalAmountBill: Amount, totalAmountCapital: Amount, expireBlockHeight: Integer, additionalBlockHeight: Integer, banker: Program, bankerKey: PublicKey) locks capitalAmount of capitalAsset { clause profit(amountBill: Amount, saver: Program) { verify above(expireBlockHeight) define sAmountBill: Integer = amountBill/100000000 define sTotalAmountBill: Integer = totalAmountBill/100000000 verify sAmountBill > 0 && sTotalAmountBill > 0 && amountBill < totalAmountBill define gain: Integer = totalAmountCapital*sAmountBill/sTotalAmountBill verify gain > 0 && gain <= capitalAmount if gain < capitalAmount { lock amountBill of assetBill with banker lock gain of capitalAsset with saver lock capitalAmount - gain of capitalAsset with FixedLimitProfit(assetBill, totalAmountBill, totalAmountCapital, expireBlockHeight, additionalBlockHeight, banker, bankerKey) } else { lock amountBill of assetBill with banker lock capitalAmount of capitalAsset with saver } } clause cancel(bankerSig: Signature) { verify above(additionalBlockHeight) verify checkTxSig(bankerKey, bankerSig) unlock capitalAmount of capitalAsset } }
合約的源代碼說明能夠具體參考Equity合約介紹
.mysql
2.5
分鐘)8
, 即 1BTM = 100000000 neu
,正常狀況下參與計算都是以neu
爲單位的,然而虛擬機的int64
類型的最大值是9223372036854775807
,爲了不數值太大致使計算溢出,因此對計算的金額提出了金額限制(即amountBill/100000000
)clause cancel
是項目方的管理方法,若是儲蓄或者取現沒有滿額,項目方也能夠回收剩餘的資產編譯Equity
合約能夠參考一下Equity
編譯器的介紹說明。假如儲蓄合約FixedLimitCollect
的參數以下:git
assetDeposited :c6b12af8326df37b8d77c77bfa2547e083cbacde15cc48da56d4aa4e4235a3ee totalAmountBill :10000000000 totalAmountCapital :20000000000 dueBlockHeight :1070 expireBlockHeight :1090 additionalBlockHeight :1100 banker :0014dedfd406c591aa221a047a260107f877da92fec5 bankerKey :055539eb36abcaaf127c63ae20e3d049cd28d0f1fe569df84da3aedb018ca1bf
其中bankerKey
是管理員的publicKey
,能夠經過比原鏈的接口list-pubkeys
來獲取,注意管理員須要保存一下對應的rootXpub
和Path
,不然沒法正確調用clause cancel
。github
實例化合約命令以下:算法
// 儲蓄合約 ./equity FixedLimitCollect --instance c6b12af8326df37b8d77c77bfa2547e083cbacde15cc48da56d4aa4e4235a3ee 10000000000 20000000000 1070 1090 1100 0014dedfd406c591aa221a047a260107f877da92fec5 055539eb36abcaaf127c63ae20e3d049cd28d0f1fe569df84da3aedb018ca1bf // 取現合約 ./equity FixedLimitProfit --instance c6b12af8326df37b8d77c77bfa2547e083cbacde15cc48da56d4aa4e4235a3ee 10000000000 20000000000 1090 1100 0014dedfd406c591aa221a047a260107f877da92fec5 055539eb36abcaaf127c63ae20e3d049cd28d0f1fe569df84da3aedb018ca1bf
發佈合約交易即將資產鎖定到合約中。因爲目前沒法在比原的dashboard
上構造合約交易,因此須要藉助外部工具來發送合約交易,好比postman
。按照上述示意圖所示,項目方須要發佈1000
個儲蓄資產的儲蓄合約和200
個利息資產取現合約。假設項目方須要發佈1000
個儲蓄資產(假如精度爲8
,那麼1000
個在比原鏈中表示爲100000000000
)的鎖倉合約,那麼他須要將對應數量的票據鎖定在儲蓄合約中,其交易模板以下:sql
{ "base_transaction": null, "actions": [ { "account_id": "0ILGLSTC00A02", "amount": 20000000, "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "type": "spend_account" }, { "account_id": "0ILGLSTC00A02", "amount": 100000000000, "asset_id": "13016eff73ffb7539a69e122f80f5c1cc94446773ac3f64dec290429f87e73b3", "type": "spend_account" }, { "amount": 100000000000, "asset_id": "13016eff73ffb7539a69e122f80f5c1cc94446773ac3f64dec290429f87e73b3", "control_program": "20055539eb36abcaaf127c63ae20e3d049cd28d0f1fe569df84da3aedb018ca1bf160014dedfd406c591aa221a047a260107f877da92fec5024c04024204022e040500c817a8040500e40b540220c6b12af8326df37b8d77c77bfa2547e083cbacde15cc48da56d4aa4e4235a3ee4d3b02597a642f0200005479cda069c35b797ca153795579a19a695a790400e1f5059653790400e1f505967c00a07c00a09a69c35b797c9f9161644d010000005b79c2547951005e79895d79895c79895b7989597989587989537a894caa587a649e0000005479cd9f6959790400e1f5059653790400e1f505967800a07800a09a5c7956799f9a6955797b957c96c37800a052797ba19a69c3787c9f91616487000000005b795479515b79c1695178c2515d79c16952c3527994c251005d79895c79895b79895a79895979895879895779895679890274787e008901c07ec1696399000000005b795479515b79c16951c3c2515d79c16963aa000000557acd9f69577a577aae7cac890274787e008901c07ec169515b79c2515d79c16952c35c7994c251005d79895c79895b79895a79895979895879895779895679895579890274787e008901c07ec169632a020000005b79c2547951005e79895d79895c79895b7989597989587989537a894caa587a649e0000005479cd9f6959790400e1f5059653790400e1f505967800a07800a09a5c7956799f9a6955797b957c96c37800a052797ba19a69c3787c9f91616487000000005b795479515b79c1695178c2515d79c16952c3527994c251005d79895c79895b79895a79895979895879895779895679890274787e008901c07ec1696399000000005b795479515b79c16951c3c2515d79c16963aa000000557acd9f69577a577aae7cac890274787e008901c07ec16951c3c2515d79c169633b020000547acd9f69587a587aae7cac747800c0", "type": "control_program" } ], "ttl": 0, "time_range": 1521625823 }
合約交易成功後,合約control_program
對應的UTXO
將會被全部用戶查詢到,使用比原鏈的接口list-unspent-outputs
便可查詢。chrome
此外,開發者須要存儲一下合約UTXO
的assetID
和program
,以便在DAPP
的前端頁面的config
配置文件和bufferserver
緩衝服務器中調用。如上所示:數據庫
// 儲蓄合約 assetID:13016eff73ffb7539a69e122f80f5c1cc94446773ac3f64dec290429f87e73b3 program:20055539eb36abcaaf127c63ae20e3d049cd28d0f1fe569df84da3aedb018ca1bf160014dedfd406c591aa221a047a260107f877da92fec5024c04024204022e040500c817a8040500e40b540220c6b12af8326df37b8d77c77bfa2547e083cbacde15cc48da56d4aa4e4235a3ee4d3b02597a642f0200005479cda069c35b797ca153795579a19a695a790400e1f5059653790400e1f505967c00a07c00a09a69c35b797c9f9161644d010000005b79c2547951005e79895d79895c79895b7989597989587989537a894caa587a649e0000005479cd9f6959790400e1f5059653790400e1f505967800a07800a09a5c7956799f9a6955797b957c96c37800a052797ba19a69c3787c9f91616487000000005b795479515b79c1695178c2515d79c16952c3527994c251005d79895c79895b79895a79895979895879895779895679890274787e008901c07ec1696399000000005b795479515b79c16951c3c2515d79c16963aa000000557acd9f69577a577aae7cac890274787e008901c07ec169515b79c2515d79c16952c35c7994c251005d79895c79895b79895a79895979895879895779895679895579890274787e008901c07ec169632a020000005b79c2547951005e79895d79895c79895b7989597989587989537a894caa587a649e0000005479cd9f6959790400e1f5059653790400e1f505967800a07800a09a5c7956799f9a6955797b957c96c37800a052797ba19a69c3787c9f91616487000000005b795479515b79c1695178c2515d79c16952c3527994c251005d79895c79895b79895a79895979895879895779895679890274787e008901c07ec1696399000000005b795479515b79c16951c3c2515d79c16963aa000000557acd9f69577a577aae7cac890274787e008901c07ec16951c3c2515d79c169633b020000547acd9f69587a587aae7cac747800c0 // 取現合約 assetID:c6b12af8326df37b8d77c77bfa2547e083cbacde15cc48da56d4aa4e4235a3ee program:20055539eb36abcaaf127c63ae20e3d049cd28d0f1fe569df84da3aedb018ca1bf160014dedfd406c591aa221a047a260107f877da92fec5024c040242040500c817a8040500e40b540220c6b12af8326df37b8d77c77bfa2547e083cbacde15cc48da56d4aa4e4235a3ee4caa587a649e0000005479cd9f6959790400e1f5059653790400e1f505967800a07800a09a5c7956799f9a6955797b957c96c37800a052797ba19a69c3787c9f91616487000000005b795479515b79c1695178c2515d79c16952c3527994c251005d79895c79895b79895a79895979895879895779895679890274787e008901c07ec1696399000000005b795479515b79c16951c3c2515d79c16963aa000000557acd9f69577a577aae7cac747800c0
比原鏈的DAPP
整體框架模型描述了DAPP
的大體結構模型,結合儲蓄分成合約案例,其具體流程以下:npm
儲蓄分成合約前端邏輯處理流程大體以下:
比原的chrome
插件源碼位於Bytom-JS-SDK,開發比原DAPP
時調用插件的說明能夠參考Dapp Developer Guide
2)配置合約參數
該Dapp demo
中須要配置實例化的參數爲assetDeposited
、totalAmountBill
、totalAmountCapital
、dueBlockHeight
、expireBlockHeight
、additionalBlockHeight
、banker
、bankerKey
。其前端配置文件爲configure.json.js
var config = { "solonet": { "depositProgram": "2091194ddbf3614cafbadb1274c33e61afd4d5044c6ec4c30f8202980199c30083160014c800033d5e94de5f22e23a6d3cbeaed87b55bd640600204aa9d101050010a5d4e8203310d9951697418af3cdbe7a9cdde1dc49bb5439503dacb33828d6c9ef5af5a24dfc01567a64f5010000c358797ca153795579a19a6957790400e1f5059653790400e1f505967c00a07c00a09a69c358797c9f91616429010000005879c2547951005b79895a7989597989587989537a894c9a567a649300000057790400e1f5059653790400e1f505967800a07800a09a5a7956799f9a6955797b957c96c37800a052797ba19a69c3787c9f9161647c0000000059795479515979c1695178c2515b79c16952c3527994c251005b79895a79895979895879895779895679890274787e008901c07ec169638e0000000059795479515979c16951c3c2515b79c169639a000000567a567aae7cac890274787e008901c07ec169515879c2515a79c16952c3597994c251005a79895979895879895779895679895579890274787e008901c07ec16963f0010000005879c2547951005b79895a7989597989587989537a894c9a567a649300000057790400e1f5059653790400e1f505967800a07800a09a5a7956799f9a6955797b957c96c37800a052797ba19a69c3787c9f9161647c0000000059795479515979c1695178c2515b79c16952c3527994c251005b79895a79895979895879895779895679890274787e008901c07ec169638e0000000059795479515979c16951c3c2515b79c169639a000000567a567aae7cac890274787e008901c07ec16951c3c2515a79c16963fc010000567a567aae7cac747800c0", "profitProgram": "2091194ddbf3614cafbadb1274c33e61afd4d5044c6ec4c30f8202980199c30083160014c800033d5e94de5f22e23a6d3cbeaed87b55bd640600204aa9d101050010a5d4e820666f298d34806a6db0528c3b3d081bc00fa58aa393e7c42f90d67eb7db2a524f4c9a567a649300000057790400e1f5059653790400e1f505967800a07800a09a5a7956799f9a6955797b957c96c37800a052797ba19a69c3787c9f9161647c0000000059795479515979c1695178c2515b79c16952c3527994c251005b79895a79895979895879895779895679890274787e008901c07ec169638e0000000059795479515979c16951c3c2515b79c169639a000000567a567aae7cac747800c0", "assetDeposited": "3310d9951697418af3cdbe7a9cdde1dc49bb5439503dacb33828d6c9ef5af5a2", "assetBill": "666f298d34806a6db0528c3b3d081bc00fa58aa393e7c42f90d67eb7db2a524f", "totalAmountBill": 1000000000000, "totalAmountCapital": 2000000000000, "dueBlockHeight": 0, "expireBlockHeight": 0, "banker": "0014c800033d5e94de5f22e23a6d3cbeaed87b55bd64", "gas": 0.4 }, "testnet":{ "depositProgram": "20f39af759065598406ca988f0dd79af9175dd7adcbe019317a2d605578b1597ac1600147211ec12410ce8bd0d71cab0a29be3ea61c71eb103c8260203da240203da2402060080f420e6b50600407a10f35a2000d38a1c946e8cba1a69493240f281cd925002a43b81f516c4391b5fb2ffdacd4d4302597a64370200005479cda069c35b790400e1f5059600a05c797ba19a53795579a19a695a790400e1f5059653790400e1f505967800a07800a09a6955797b957c9600a069c35b797c9f9161645b010000005b79c2547951005e79895d79895c79895b7989597989587989537a894ca4587a64980000005479cd9f6959790400e1f5059653790400e1f505967800a07800a09a5c7956799f9a6955797b957c967600a069c3787c9f91616481000000005b795479515b79c1695178c2515d79c16952c3527994c251005d79895c79895b79895a79895979895879895779895679890274787e008901c07ec1696393000000005b795479515b79c16951c3c2515d79c16963a4000000557acd9f69577a577aae7cac890274787e008901c07ec169515b79c2515d79c16952c35c7994c251005d79895c79895b79895a79895979895879895779895679895579890274787e008901c07ec1696332020000005b79c2547951005e79895d79895c79895b7989597989587989537a894ca4587a64980000005479cd9f6959790400e1f5059653790400e1f505967800a07800a09a5c7956799f9a6955797b957c967600a069c3787c9f91616481000000005b795479515b79c1695178c2515d79c16952c3527994c251005d79895c79895b79895a79895979895879895779895679890274787e008901c07ec1696393000000005b795479515b79c16951c3c2515d79c16963a4000000557acd9f69577a577aae7cac890274787e008901c07ec16951c3c2515d79c1696343020000547acd9f69587a587aae7cac747800c0", "profitProgram": "20f39af759065598406ca988f0dd79af9175dd7adcbe019317a2d605578b1597ac1600147211ec12410ce8bd0d71cab0a29be3ea61c71eb103c8260203da2402060080f420e6b50600407a10f35a20f855baf98778a892bad0371f5afca845191824dc8584585d566fbbc8ef1f304c4ca4587a64980000005479cd9f6959790400e1f5059653790400e1f505967800a07800a09a5c7956799f9a6955797b957c967600a069c3787c9f91616481000000005b795479515b79c1695178c2515d79c16952c3527994c251005d79895c79895b79895a79895979895879895779895679890274787e008901c07ec1696393000000005b795479515b79c16951c3c2515d79c16963a4000000557acd9f69577a577aae7cac747800c0", "assetDeposited": "00d38a1c946e8cba1a69493240f281cd925002a43b81f516c4391b5fb2ffdacd", "assetBill": "f855baf98778a892bad0371f5afca845191824dc8584585d566fbbc8ef1f304c", "totalAmountBill": 100000000000000, "totalAmountCapital": 200000000000000, "dueBlockHeight": 140506, "expireBlockHeight": 140506, "banker": "00147211ec12410ce8bd0d71cab0a29be3ea61c71eb1", "gas": 0.4 } }
以儲蓄合約FixedLimitCollect
爲例,前端須要對該合約進行verify
語句的預判斷邏輯,以防用戶輸入參數以後執行失敗。此外,合約中billAmount of billAsset
表示鎖定的資產和數量,而billAmount
、billAsset
和utxohash
都是儲存在緩衝服務器的數據表裏面,所以前端須要調用list-utxo
查找與該資產asset
和program
相關的全部未花費的utxo。 具體能夠參考DAPP DEMO
前端案例。
4)交易組成
比原的交易是多輸入多輸出的模板結構,若是合約中包含了多個lock
或unlock
語句,那麼就須要用戶構造多輸入多輸出的交易模板,同時,構造交易還須要根據lock
語句或unlock
語句來變換。交易構造具體能夠參考儲蓄合約交易模型和取現合約交易模型的前端源代碼。
交易input
結構以下:
spendUTXOAction(utxohash)
表示花費的合約utxo
,其中utxohash
表示合約UTXO
的hash
,而spendWalletAction(amount, Constant.assetDeposited)
表示用戶輸入的儲蓄或取現的數量(僅包含中須要資產交換的合約中),而資產類型則由前端固定。
export function spendUTXOAction(utxohash){ return { "type": "spend_utxo", "output_id": utxohash } } export function spendWalletAction(amount, asset){ return { "amount": amount, "asset": asset, "type": "spend_wallet" } } const input = [] input.push(spendUTXOAction(utxohash)) input.push(spendWalletAction(amount, Constant.assetDeposited))
交易output
結構以下:
根據合約中if-else
斷定邏輯,下面即是儲蓄分成合約的output
的構造模型。
export function controlProgramAction(amount, asset, program){ return { "amount": amount, "asset": asset, "control_program": program, "type": "control_program" } } export function controlAddressAction(amount, asset, address){ return { "amount": amount, "asset": asset, "address": address, "type": "control_address" } } const output = [] if(amountDeposited < billAmount){ output.push(controlProgramAction(amountDeposited, Constant.assetDeposited, Constant.profitProgram)) output.push(controlAddressAction(amountDeposited, billAsset, saver)) output.push(controlProgramAction((billAmount-amountDeposited), billAsset, Constant.depositProgram)) }else{ output.push(controlProgramAction(amountDeposited, Constant.assetDeposited, Constant.profitProgram)) output.push(controlAddressAction(billAmount, billAsset, saver)) }
5)啓動前端服務
編譯前端命令以下:
npm run build
啓動以前須要先啓動bufferserver
緩衝服務器,而後再啓動前端服務,其前端啓動命令以下:
npm start
緩衝服務器主要是爲了在管理合約UTXO
層面作一些效率方面的處理,包括了對bycoin
服務器是如何同步請求的,此外對DAPP
的相關交易記錄也進行了存儲。具體能夠參考一下bufferserver
源代碼。
1)儲蓄分成合約的架構說明以下:
3
張數據表:base
、utxo
和balance
表。其中base
表用於初始化該DAPP
關注的合約program
,即在查找utxo
集合的時候,僅僅只需過濾出對應的program
和資產便可; utxo
表是該DAPP
合約的utxo
集合,其數據是從bycoin
服務器中實時同步過來的,主要是爲了提升DAPP
的併發性; balance
表是爲了記錄用戶參與該合約的交易列表。API
進程和同步進程組成,其中API
服務進程用於管理對外的用戶請求,而同步進程包含了兩個方面:一個是從bycoin
服務器同步utxo
,另外一個是則是經過區塊鏈瀏覽器查詢交易狀態update-base
接口更新DAPP
關注的合約program
和asset
。而utxo
同步進程會根據base
表的記錄來定時掃描並更新本地的utxo
表中的信息,而且根據超時時間按期解鎖被鎖定的utxo
utxo
是否可用,可用的utxo
集合中包含了未確認的utxo
。用戶在前端在點擊儲蓄或取現按鍵的時候,會調用utxo
最優匹配算法選擇最佳的utxo
,而後調用update-utxo
接口對該utxo
進行鎖定,最後就用戶就能夠經過插件錢包調用bycoin
服務器的構建交易接口來建立交易、簽名交易和提交交易。假若全部合約utxo
都被鎖定了,則會縮短第一個utxo
的鎖定時間爲60s
,設置該時間間隔是爲了保證未確認的交易被成功驗證並生成未確認的utxo
。若是該時間間隔並無產生新的utxo
,則認爲前面一個用戶並無產生交易,則60s
後能夠再次花費該utxo
。balance
記錄表,默認狀態是失敗的,其中交易ID用於向區塊鏈瀏覽器查詢交易狀態,若是交易成功則會更新balance
的交易狀態。此外,前端頁面的balance
列表表只顯示交易成功的記錄。2)編譯bufferserver
源代碼
按照README
安裝部署服務須要的軟件包Mysql
和Redis
,而後下載源代碼並編譯:
make all
編譯完成以後,在target
目錄下會生成可執行文件api
和updater
。
3)啓動服務
使用root
用戶建立數據庫和數據表,其命令以下:
mysql -u root -p < database/dump.sql
修改配置文件config_local.json
,字段說明參考README
的config
配置參數詳解。
啓動api
和updater
服務器,其中api
是提供JSON RPC
請求的服務進程,updater
是提供同步blockcenter
和區塊鏈瀏覽器數據請求的服務進程。
./target/api config_local.json ./target/updater config_local.json