1. 程式人生 > >大型合約系統的一個構建思路與實踐

大型合約系統的一個構建思路與實踐

以太坊環境下智慧合約開發,最重要的特點便是安全:像餘額這類的資訊,是直接關係到錢的。第二個特點便是:不太適合做大型的、高複雜度合約系統。
原因有以下幾個:

  • 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的地址資訊即可。