如何編寫一個可升級的智能合約
區塊鏈信任基礎的數據不可修改的特性,讓它傳統應用程序有一個很大的不同的地方是一經發佈於區塊鏈上就無法修改(不能直接在原有的合約上直接修改再重新發布)。
寫在前面
閱讀本文前,你應該對以太坊、智能合約及Solidity語言有所了解,如果你還不了解,建議你先看以太坊是什麼
當智能合約出現bug
一方面正式由於智能合約的不可修改的特性,因為只要規則確定之後,沒人能夠修改它,大家才能夠信任它。但另一方面,如果規則的實現有Bug, 可能會造成代幣被盜,或是調用消耗大量的gas。這時就需要我們去修復錯誤。
我們知道一個智能合約包含兩部分: 代碼邏輯和數據,而代碼邏輯又是最容易出問題的部分, 如在實現如下合約時,由於手抖在寫addTen()時,10寫成了11。
pragma solidity ^0.4.18;contract MyContract { mapping (address => uint256) public balanceOf; function setBlance(address _address,uint256 v) public { balanceOf[_address] = v; } function addTen(address addr) public returns (uint){ return balanceOf[addr] + 11; }}
假如我們在部署之後發現了這個問題,想要修復這個bug的話,只好重新部署合約,可是這時會有一個尷尬的問題,原來的合約已經有很多人使用,如果部署新的合約,老合約的數據將會丟失。
數據合約及控制合約
那麼如何解決上面的問題了,一個解決方案是分離合約中的數據,使用一個單獨的合約來存儲數據(下文稱數據合約),使用一個單獨的合約寫業務邏輯(下文稱控制合約)。
我們來看看代碼如何實現。pragma solidity ^0.4.18;contract DataContract { mapping (address => uint256) public balanceOf; function setBlance(address _address,uint256 v) public { balanceOf[_address] = v; }}contract ControlContract { DataContract dataContract; function ControlContract(address _dataContractAddr) public { dataContract = DataContract(_dataContractAddr); } function addTen(address addr) public returns (uint){ return dataContract.balanceOf(addr) + 11; }}
現在我們有兩個合約DataContract 專門用來存數據,ControlContract用來處理邏輯,並利用DataContract來讀寫數據。通過這樣的設計,可以在更新控制合約後保持數據合約不變,這樣就不會丟失數據,也不用遷移數據。
讀寫控制
通過DataContract我們可以單獨更新合約邏輯,不過你也許發現了一個新的問題,DataContract的數據不僅僅可以被ControlContract讀寫,還可以被其他的合約讀寫,因此需要對DataContract添加讀寫控制。我們給DataContract添加一個mapping, 用來控制哪些地址可以訪問數據,同時添加了修飾器及設置訪問的方法,代碼如下:
pragma solidity ^0.4.18;contract DataContract { mapping (address => uint256) public balanceOf; mapping (address => bool) accessAllowed; function DataContract() public { accessAllowed[msg.sender] = true; } function setBlance(address _address,uint256 v) public { balanceOf[_address] = v; } modifier platform() { require(accessAllowed[msg.sender] == true); _; } function allowAccess(address _addr) platform public { accessAllowed[_addr] = true; } function denyAccess(address _addr) platform public { accessAllowed[_addr] = false; }}...
訂閱我的小專欄可參看合約的完整代碼。
部署方法如下:
- 先部署DataContract合約
- 使用DataContract合約地址作為部署ControlContract合約的參數
- 用ControlContract合約地址作為參數調用DataContract合約的allowAccess方法。
如果需要更新控制合約(如修復了addTen)則重新執行第2-3步,同時對老的控制合約執行denyAccess()。
更多
當我們在實現數據合約時,它包含的邏輯應該越少越好,並且應該是嚴格測試過的,因為一旦數據合約部署之後,就沒法更改。
大多數情況下,和用戶交互的是DApp, 因此當控制合約升級之後,需要升級DApp,使之關聯新的控制合約。儘管合約可以通過本文的方式升級,但我們依然要謹慎升級,因為升級表示你可以重寫邏輯,會降低用戶對你的信任度。
本文介紹升級方法更多的是一種思路,實際項目中可能會對應多個控制合約及數據合約。歡迎來我的知識星球討論區塊鏈技術。
深入淺出區塊鏈 - 系統學習區塊鏈,打造最好的區塊鏈技術博客。
推薦閱讀: