智能合約的現狀
以太坊在區塊鏈上實現了智能合約的概念,用於:同質化通證發行(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-demo