近期開發(fā)基于以太坊的智能合約,為實(shí)驗(yàn)室的分布式電商系統(tǒng)提供可信的“第三方信用擔(dān)?!惫δ堋?/span>
由于初期項(xiàng)目需求變動(dòng)會(huì)比較頻繁,并且智能合約一經(jīng)發(fā)布于區(qū)塊鏈上就無法修改,即使智能合約中有Bug需要修復(fù)或者業(yè)務(wù)邏輯變更,它也不能直接在原有的合約上直接修改再重新發(fā)布。因此在即將發(fā)布第一版之前,需要結(jié)合業(yè)務(wù)場(chǎng)景考慮合理的升級(jí)改造機(jī)制。
初版的智能合約主要包括如下功能:
1. 買家創(chuàng)建訂單,合約記錄訂單基本信息并且需要買方向合約支付交易費(fèi)用
2. 賣家取消訂單,賣家在發(fā)貨前可以取消訂單,合約自動(dòng)將交易費(fèi)用退回給買家
3. 賣家發(fā)貨,合約記錄相應(yīng)訂單的物流信息,修改訂單狀態(tài)
4. 買家確認(rèn)收貨,合約將交易費(fèi)用轉(zhuǎn)移至賣家,并且修改訂單狀態(tài)為已完成
5. 基本的訂單查詢功能
通過簡單的需求介紹,可以看出合約中會(huì)保存著交易費(fèi)用,訂單數(shù)據(jù)以及基本的電商交易流程動(dòng)作。
設(shè)計(jì)方案
起初寫的智能合約全部集中在一個(gè)contract,里面會(huì)保存交易費(fèi)用,訂單數(shù)據(jù)以及電商交易的全部流程。一旦發(fā)布就不能修改(哪怕是添加一個(gè)方法,修改方法邏輯以及數(shù)據(jù)中添加新的字段,,,),因?yàn)橹匦掳l(fā)布新的合約之后,之前的訂單數(shù)據(jù)以及交易費(fèi)用全部在舊的合約中,從工程的角度來看,同時(shí)維護(hù)新舊合約(也許好多個(gè)舊合約,,,,)簡直不能更糟糕。
因此決定將業(yè)務(wù)邏輯和數(shù)據(jù)從合約代碼層面就做好分離,即合約分為了兩類:邏輯合約以及數(shù)據(jù)合約。邏輯合約通過訪問數(shù)據(jù)合約獲得數(shù)據(jù),并對(duì)數(shù)據(jù)做邏輯處理,然后寫回?cái)?shù)據(jù)合約,專注于對(duì)數(shù)據(jù)的邏輯處理和對(duì)外提供服務(wù)。邏輯合約不存儲(chǔ)任何狀態(tài);數(shù)據(jù)合約專注于數(shù)據(jù)結(jié)構(gòu)定義與所存儲(chǔ)數(shù)據(jù)的讀寫接口。
邏輯合約與數(shù)據(jù)合約存在操作關(guān)系,邏輯上分為四類:
1. 邏輯合約與數(shù)據(jù)合約 1對(duì)1
2. 邏輯合約與數(shù)據(jù)合約 1對(duì)多
3. 邏輯合約與數(shù)據(jù)合約 多對(duì)1
4. 邏輯合約與數(shù)據(jù)合約 多對(duì)多
根據(jù)本項(xiàng)目的業(yè)務(wù)場(chǎng)景,采取邏輯合約與數(shù)據(jù)合約1對(duì)1的關(guān)系(后續(xù)業(yè)務(wù)復(fù)雜,可以改造為多對(duì)1),綜上,初期的設(shè)計(jì)圖如下:
首先進(jìn)行數(shù)據(jù)與邏輯的分離。
數(shù)據(jù)合約
數(shù)據(jù)合約包括狀態(tài),以及每個(gè)狀態(tài)對(duì)應(yīng)讀寫方法(set&&get),部分代碼如下:
邏輯合約
邏輯合約因?yàn)閮H包含業(yè)務(wù)邏輯,不會(huì)存儲(chǔ)任何的數(shù)據(jù)狀態(tài),因此使用Solidity的library進(jìn)行編寫。
Solidity的library
library的好處是它是單實(shí)例,只會(huì)更新一個(gè)library文件,不會(huì)像contract那樣因?yàn)楦鞣N依賴產(chǎn)生‘漣漪’效應(yīng),對(duì)于經(jīng)常變動(dòng)的業(yè)務(wù)邏輯代碼,使用library每次更新會(huì)節(jié)省大量gas消耗。
library是需要被合約調(diào)用才能執(zhí)行的,調(diào)用library的contract(下面簡稱A),使用的delegatecall方法,因此library執(zhí)行的上下文環(huán)境在A中,也就是library的msg.sender是A,并且可以直接修改A中的storage。直白說,A調(diào)用library,就是把library的代碼import到自己的內(nèi)部執(zhí)行了。
入口合約
因?yàn)闃I(yè)務(wù)邏輯合約使用了library編寫,library的調(diào)用是需要合約觸發(fā)的,因此需要一個(gè)入口合約,來去調(diào)用library操作數(shù)據(jù)合約,而且對(duì)于本項(xiàng)目的實(shí)際需求,上面將數(shù)據(jù)以及邏輯進(jìn)行分離后,忘記了一個(gè)重要的方面,就是資金存儲(chǔ),引入入口合約后,正好解決了這個(gè)問題,客戶端調(diào)用入口合約代碼,將資金充入入口合約,并且由入口合約進(jìn)行資金的轉(zhuǎn)移過程。
入口合約調(diào)用library來去操作數(shù)據(jù)合約的數(shù)據(jù),因此在入口合約中,需要引入調(diào)用的library(或者是library的interface),這樣子,如果library添加一個(gè)新的方法,不僅library需要更新,入口合約也是需要更新的,而入口合約的作用包括交易資金托管以及調(diào)用library方法,更新發(fā)生時(shí),需要有適當(dāng)?shù)姆椒▽⑴f的入口合約資金轉(zhuǎn)移到新的入口合約中,因此寫了kill函數(shù),當(dāng)更新發(fā)生時(shí),將新的合約地址作為參數(shù),執(zhí)行如下舊合約的kill方法:
Solidity的using關(guān)鍵詞
上面反復(fù)提到“入口合約調(diào)用library來去操作數(shù)據(jù)合約的數(shù)據(jù)”,這個(gè)地方使用到了using這個(gè)Solidiy關(guān)鍵詞。
using關(guān)鍵詞是Solidity的contract引用library時(shí)使用,例子如下:
pragma solidity ^0.4.15;
library SomeLibrary? {
?function add(uint self, uint b) returns (uint) {
?? return self+b;
?}
}
contract SomeContract {
?? ?
??? using SomeLibrary for uint;
?? ?
??? function add3(uint number) returns (uint) {
??????? return number.add(3);?? ?
??? }
}
using SomeLibrary for uint 意思是在SomContract合約中,uint類型的參數(shù)可以直接調(diào)用SomeLibrary的方法,并且將uint自身作為第一個(gè)參數(shù)傳遞到library的方法中。
回到項(xiàng)目中,“入口合約調(diào)用library來去操作數(shù)據(jù)合約的數(shù)據(jù)”,即調(diào)用library方法的同時(shí),需要將數(shù)據(jù)合約作為參數(shù)提供給library,因此代碼可以寫成如下:
在入口合約初始化的時(shí)候,將數(shù)據(jù)合約賦值給tradeData,因?yàn)橛辛藆sing LogicLibrary for address, 因此address類型的tradeData可以直接調(diào)用library的方法,并且將其自身(數(shù)據(jù)合約的地址)傳遞給了library。
合約間調(diào)用的return問題
按照上面的方式改造,在測(cè)試過程中,發(fā)現(xiàn)當(dāng)執(zhí)行查詢數(shù)據(jù)的時(shí)候,合約間返回非定長類型的string,會(huì)出錯(cuò),上stackexchange的Ethereum板塊中,找到如下解答:
也就是說Solidity對(duì)于合約外部調(diào)用,返回變長類型的字段,EVM是不支持的?;氐巾?xiàng)目本身,因?yàn)橛唵螖?shù)據(jù)中定義了多個(gè)string類型數(shù)據(jù),沒有辦法去改變數(shù)據(jù)結(jié)構(gòu),因此采取折中的辦法,對(duì)于查詢操作,客戶端直接讀取數(shù)據(jù)合約(缺點(diǎn)是目前的設(shè)計(jì)方案數(shù)據(jù)合約是不可改的,但是對(duì)于查詢,可以將數(shù)據(jù)合約中全部狀態(tài)寫好get方法,并由客戶端對(duì)返回的狀態(tài)進(jìn)行整理是可以滿足需求的)。
部署
本項(xiàng)目智能合約使用了Truffle進(jìn)行開發(fā)編寫,對(duì)于升級(jí)改造之后的部署腳本,代碼如下:
對(duì)于Library與Contract的關(guān)聯(lián),是通過deployer.link實(shí)現(xiàn)在字節(jié)碼級(jí)別上的,并不需要合約代碼顯式寫出來。
最終架構(gòu)
經(jīng)過上述實(shí)踐,智能合約成功完成可升級(jí)的改造,架構(gòu)圖如下:
經(jīng)過上面改造,基本滿足實(shí)驗(yàn)室分布式電商智能合約的升級(jí)需求,但是這種方式應(yīng)用到其他實(shí)際場(chǎng)景中,可能存在的問題包括數(shù)據(jù)合約無法修改;讀寫操作客戶端需要對(duì)兩個(gè)合約進(jìn)行操作;缺少proxy合約(使得智能合約升級(jí)對(duì)于客戶端dapp無感知)等。所以智能合約的升級(jí)改造,還是要和實(shí)際需求相結(jié)合,此文盡量滿足本身項(xiàng)目需求的同時(shí),使用業(yè)內(nèi)通用方法來去實(shí)踐,若有不足,望批評(píng)指正。
參考文章
1. Writing upgradable contracts in Solidity:https://blog.colony.io/writing-upgradeable-contracts-in-solidity-6743f0eecc88
2. Solidity’s ‘using’ keyword:https://medium.com/@gus_tavo_guim/soliditys-using-keyword-c05c18aaa088
3. One reason to start using Solidity Libraries:
出處:智能合約的可升級(jí)改造實(shí)踐/#背景起因
版權(quán)申明:內(nèi)容來源網(wǎng)絡(luò),版權(quán)歸原創(chuàng)者所有。除非無法確認(rèn),我們都會(huì)標(biāo)明作者及出處,如有侵權(quán)煩請(qǐng)告知,我們會(huì)立即刪除并表示歉意。謝謝。
-END-
架構(gòu)文摘
ID:ArchDigest
互聯(lián)網(wǎng)應(yīng)用架構(gòu)丨架構(gòu)技術(shù)丨大型網(wǎng)站丨大數(shù)據(jù)丨機(jī)器學(xué)習(xí)
評(píng)論