大型合約系統的一個構建思路與實踐
阿新 • • 發佈:2018-11-10
以太坊環境下智慧合約開發,最重要的特點便是安全:像餘額這類的資訊,是直接關係到錢的。第二個特點便是:不太適合做大型的、高複雜度合約系統。
原因有以下幾個:
- 1 . 除錯困難。雖然有truffle這種利器,讓釋出合約、測試合約能夠用js指令碼自動執行,但是出錯了排查問題依然是一件非常麻煩的事情,尤其是當合約變多、合約間的呼叫變多,除錯、排錯難度急劇增長。
- 2 . 程式碼越多隱藏的bug越多。尤其是合約間的相互呼叫、許可權處理。
- 3 . 合約個數增多,升級合約時所需處理的細節越複雜、易出錯。
假如我們非要逆勢而為,必須對上述幾個問題給出良好的解決方案。
我們在專案中使用了一種以資料為中心的合約結構,並設一白名單控制該資料合約的訪問許可權(只對set類方法設防,get類不做限制)。改資料合約在整個專案的生存期中,是不可升級的。
為了讓DataContract能夠存放足夠的資料,我們可以為各個資料類中宣告一個mapping,如
mapping(uint => uint) m_uintMapUint;
便可以存放任意多的uint變數,只要各個合約之間事先定好index的含義即可。為了方便值的取用,建議使用bytes32做mapping的key:
mapping(bytes32 => uint) m_uintMap;
m_uintMap["varA"] = 10;
一般來說,一個特定的工程當中用的資料型別就那麼幾種,多生名幾個mapping便可滿足需求了。
接下來是要解決資料合約的訪問許可權控制。可以使用一個(address => bool)的mapping記錄一個白名單,然後還需要一個modifier來放在資料合約中每個set類方法的後面。最終的DataContract是這個樣子的:
contract MyDataContract{ mapping (bytes32 => uint256) public m_byte32_uint256; mapping (bytes32 => address) public m_byte32_address; mapping (bytes32 => mapping (address => uint256)) public m_byte32_address_uint256; mapping (address => bool) public m_whiteList; address public m_manager; modifier onlyManager{ require(msg.sender == m_manager); _; } modifier isLegalCall{ require(m_whiteList[msg.sender]); _; } constructor(){ m_manager = msg.sender; } function addToWhiteList(address _addr)public onlyManager{ m_whiteList[_addr] = true; } function removeFromWhiteList(address _addr)public onlyManager{ m_whiteList[_addr] = false; } function set_byte32_uint256(bytes32 _index, uint256 _val)public isLegalCall{ m_byte32_uint256[_index] = _val; } }
接下來是控制合約的結構。控制合約一般會有多個,用來實現具體業務邏輯,並且需要能夠升級(用來修復bug)。控制合約除了依賴data合約之外,也可能相互依賴。假設B合約依賴A合約的某個方法,則B合約中必然有個變數儲存有A合約的地址,升級控制合約A時,一併升級B合約中A的地址資訊即可。