【ETH智能合约系列】04--可升级的智能合约
当智能合约出现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;
}
}
部署方法如下:
1、先部署DataContract合约
2、使用DataContract合约地址作为部署ControlContract合约的参数
3、用ControlContract合约地址作为参数调用DataContract合约的allowAccess方法。
如果需要更新控制合约(如修复了addTen)则重新执行第2-3步,同时对老的控制合约执行denyAccess()。