大型合约系统的一个构建思路与实践

以太坊环境下智能合约开发,最重要的特点便是安全:像余额这类的信息,是直接关系到钱的。第二个特点便是:不太适合做大型的、高复杂度合约系统。
原因有以下几个:

  • 1 . 调试困难。虽然有truffle这种利器,让发布合约、测试合约能够用js脚本自动运行,但是出错了排查问题依然是一件非常麻烦的事情,尤其是当合约变多、合约间的调用变多,调试、排错难度急剧增长。
  • 2 . 代码越多隐藏的bug越多。尤其是合约间的相互调用、权限处理。
  • 3 . 合约个数增多,升级合约时所需处理的细节越复杂、易出错。

假如我们非要逆势而为,必须对上述几个问题给出良好的解决方案。
我们在项目中使用了一种以数据为中心的合约结构,并设一白名单控制该数据合约的访问权限(只对set类方法设防,get类不做限制)。改数据合约在整个项目的生存期中,是不可升级的。
大型合约系统的一个构建思路与实践_第1张图片

为了让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的地址信息即可。

你可能感兴趣的:(BlockChain,Solidity)