【智慧合約】編寫複雜業務場景下的智慧合約——可升級的智慧合約設計模式(附Demo)
智慧合約的現狀
以太坊在區塊鏈上實現了智慧合約的概念,用於:同質化通證發行(ERC-20)、眾籌、投票、存證取證等等,共同點是:合約邏輯簡單,只是業務流程中的關鍵節點,而非整個業務流程。而智慧合約想解決的信任傳遞,是環環相扣的,如果在傳統系統環節被惡意侵入和篡改資料,那麼傳入智慧合約的資料就是不受到信任的。因此,整體業務流程上鍊是智慧合約發展的趨勢。
智慧合約的侷限
智慧合約在早期被設計的時候,並不打算支撐複雜的業務體系,這和它設計的初衷相違背:漏洞往往出現在程式設計師編寫的程式碼和他想實現的邏輯之間存在著差距,越是簡單的程式碼越是安全。簡單和受限訪問成了智慧合約安全可靠的保障。
因此,智慧合約引入了隔離網路和檔案系統的沙箱環境、基於棧的編譯器(有限高度的棧深以及僅可訪問棧頂16個元素的限制)、靜態語言、gasLimit(限定了合約的大小,每個合約能處理的邏輯有限;限定每個函式邏輯的複雜度,每一步操作都會消耗gas,以至於連使用迴圈都成了奢侈)、嚴格的記憶體訪問限制(每個合約僅可以訪問自己的儲存單元),這就導致了智慧合約不同於傳統程式語言,自身就帶著諸多限制。
目前,智慧合約仍然處於發展的早期階段,配套的工具、成熟的框架、第三方包寥寥可數。因此編寫複雜業務場景的智慧合約,只能從底層的邏輯開始編寫:編寫資料庫模型CURD、跨合約資料互動、增強基本資料型別功能(string型別的slice、array的delete),導致開發合約速度的緩慢。
另外,由於區塊鏈的另外一個特性:防篡改。這導致了智慧合約部署上鍊後,任何人包括合約所有者都不能再進行修改。意味著智慧合約無法像傳統應用那樣實現敏捷開發,合約的每一個方法都需要進過大量測試,保證整個合約的正確性嚴謹性。即使保證合約不出問題,但業務的需求並非一成不變,業務變動,智慧合約無可避免的跟著變動,那麼意味著合約的重新部署,但是舊合約的資料是無法轉移到新合約上的;已部署好的合約如果存在漏洞被惡意攻擊,需要有方法能夠儘快停止合約執行,保證使用者資料不被篡改,留出時間讓智慧合約的編寫者快速修復漏洞。因此,合約的升級和管理需要設計。
智慧合約目前的發展方向
編寫智慧合約的程式設計師目前分為兩派,一派主張合約儘可能的精簡,只有簡單才不容易出現錯誤,只有關鍵部分上鍊部分;另一派主張信任傳遞的閉環,也就是說整個業務邏輯儘可能多的上鍊,例如Polymath,ConsenSys,它們將完整的業務流程利用智慧合約的方式實現。
輕量級的智慧合約並沒有太多的技術難點可講,真正需要發展的是智慧合約設計模式。
目前,已有的官方推薦的工具集, Zeppelin,它提供了:
- 數學計算
- 合約所有權
- 編碼解碼
- 加密解密
方便合約編寫者呼叫,但這些只能是以工具、參考的形式存在,並不能算作真正意義上的設計模式。目前Zeppelin社群也在積極探索智慧合約設計模式的實現方式。
Zeppelin社群目前構思了智慧合約與邏輯分離的設計模式,用於解決智慧合約升級的問題。
原文地址: https://blog.openzeppelin.com/proxy-libraries-in-solidity-79fbe4b970fd/
如何利用智慧合約實現複雜的業務場景
目前受限於智慧合約的限制,只能實現業務場景中的關鍵步驟,如果將整個業務放到鏈上執行,傳統智慧合約的編寫方式將不再適用,例如無法解決合約檔案大小的瓶頸、引數過多導致棧深錯誤、合約之間互相訪問儲存資料的問題等等,這些都會影響智慧合約的編寫。目前如何編寫能夠承載負責業務場景的智慧合約,已經成為行業共同面臨的挑戰之一。
在這裡,我參照傳統MVC設計模式、基於關係型資料庫儲存,結合智慧合約自身特性,Zeppelin社群提供的合約分離理念,初步實現複雜業務場景的智慧合約設計模式。
一、針對業務功能處理
每個業務場景包括多個角色,角色既有單獨操作,也有與其他角色共同配合的操作。
針對角色,將其合約拆分成為:入口合約、儲存合約、邏輯合約。
1) 設計儲存合約,用於儲存角色模型的資料,並提供對外訪問的CURD方法,實現效果等同於MVC中的Model層。
2) 設計邏輯實現合約,利用儲存合約提供的CURD,實現業務邏輯。類比於MVC中的Controller層。
3) 設計入口合約,用於提供對外訪問的地址,將使用者的請求轉發至邏輯合約進行處理,執行的結果存入儲存合約中。
二、針對業務變動及風險處理
考慮到業務會發生變動。將傳統智慧合約拆分成:入口合約、儲存合約、邏輯合約,三個合約各司其職,共同實現業務,當業務發生變動需要修改,修改並重新部署邏輯合約,並將新版本的邏輯合約註冊到入口合約,即可以解決
1、 合約可實現性:突破合約大小限制、函式複雜度限制
2、 合約可升級性:地址不變,只對合約功能進行升級
3、 合約可維護性:發現bug時,及時對合約進行修復
另外,智慧合約繼承自功能開關合約,可以實現當智慧合約發現漏洞時,緊急關閉某些功能(例如轉賬),減少損失。
三、針對業務角色之間的互動處理
智慧合約中,資料的記錄都是Key-Value形式,類似於Redis這樣的資料庫,資料之間的關聯較弱。通過設定各個合約都可以訪問的全域性儲存合約,記錄各個入口合約的地址、合約資料之間的關聯關係,使各個合約之間可以資料互通。
四、針對業務場景中的執行許可權處理
智慧合約繼承自所有權合約,可以限制合約中某些關鍵方法的操作者,這些操作者可以是個人賬戶、也可以是合約賬戶,使合約受控於系統管理員。另外,也提供了許可權轉移功能,可以方便的將許可權轉移給其他管理員。
智慧合約設計模式的技術點
l 委託呼叫
以上智慧合約的拆分,就是依賴智慧合約中委託呼叫的特性。
委託呼叫,會保留呼叫者的賬戶與資訊,例如User呼叫合約A中funcA,funcA委託呼叫合約B中的funcB,那麼funcB的呼叫者就是User,而不是合約A。委託呼叫的優勢就是可以保留呼叫合約的上下文,只是利用合約B的程式碼實現想要的功能。這樣可以:
- 減少合約A中的程式碼量。
- 合約B中的邏輯可以隨時更新。
l Fallback機制
當呼叫智慧合約中未定義的方法時,智慧合約會將呼叫者及引數都傳給一個錯誤處理函式,類似訪問了網站中不存在的頁面,會跳轉到404頁面一樣。正是利用了這個特性,合約A(上文例子)將在這個fallback函式中統一處理這個未定義方法。
l 內聯彙編
智慧合約中的委託呼叫,只會返回呼叫結果是True和False,但我們要達成智慧合約的拆分,就要讓委託呼叫返回呼叫後的結果,這就需要修改委託呼叫的指令集,將結果返回。通過內聯彙編,修改智慧合約中委託呼叫的實現。
l 全域性儲存合約
全域性儲存合約是模擬傳統key-value資料庫,通過智慧合約的方式實現資料庫的CURD,將系統的配置(比如管理員的地址、Token與穩定貨幣的兌換比例等)、各個模組入口合約的地址、合約之間的關聯關係儲存起來,打通各個合約之間的資料。
l 合約的合理拆分
目前將合約拆分為入口合約、儲存合約、邏輯合約。
入口合約:所有與合約的互動都是通過入口合約。入口合約記錄了儲存合約地址:通過委託呼叫轉發給邏輯合約處理,修改儲存合約資料。記錄了邏輯合約的地址和版本:知道該轉發給哪個版本的邏輯合約處理。
儲存合約:負責儲存資料,合約的儲存結構不能變,這是底線。類比資料庫中的表,一旦設定就不能輕易修改;訪問及修改資料的介面,其他合約不能直接訪問當前合約的資料,需要通過外部函式來訪問和修改,例如java model中的setter和getter 方法,實現儲存合約的的CURD。
邏輯合約:負責處理合約邏輯,通過組合儲存合約的CURD,實現複雜的邏輯。
智慧合約框架
l 模組框架
1、 使用者呼叫入口合約函式。
2、 入口合約委託給邏輯合約處理。
3、 邏輯合約進入到入口合約上下文,獲取到儲存合約地址,修改/查詢儲存合約資料。
4、 邏輯合約返回資料給入口合約。
5、 入口合約返回資料給使用者。
l 整體框架
智慧合約設計模式的優缺點
優點:
a) 拆分智慧合約,可以繞過合約大小的限制,實現複雜的功能。
b) 可以通過升級邏輯合約來更新智慧合約。
c) 可控的智慧合約,當出現問題時,管理員賬戶可以關閉關鍵性操作。
d) 將功能性合約封裝成通用合約,減少重複部署合約消耗的gas。
e) 通用全域性儲存,可以滿足任意格式資料的儲存與讀取。
f) 合約登錄檔,可以方便合約之間的互相呼叫。
缺點:
a) 拆分智慧合約,合約總體程式碼量增加,增加了部署時gas的消耗。
b) 合約的可讀性大幅下降,使用者無法簡單的讀取合約的邏輯。
c) 鍵值對的儲存合約操作複雜。【智慧合約】編寫複雜業務場景下的智慧合約——可升級的智慧合約設計模式
Demo地址:https://github.com/NoharaHiroshi/upgradability-solidity-