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