如何编写一个可升级的智能合约

本文首发于深刻浅出区块链社区 原文连接:如何编写一个可升级的智能合约原文已更新,请读者前往原文阅读app

区块链信任基础的数据不可修改的特性,让它传统应用程序有一个很大的不一样的地方是一经发布于区块链上就没法修改(不能直接在原有的合约上直接修改再从新发布)。学习

写在前面

阅读本文前,你应该对以太坊、智能合约及Solidity语言有所了解,若是你还不了解,建议你先看以太坊是什么区块链

当智能合约出现bug

一方面正式因为智能合约的不可修改的特性,由于只要规则肯定以后,没人可以修改它,你们才可以信任它。但另外一方面,若是规则的实现有Bug, 可能会形成代币被盗,或是调用消耗大量的gas。这时就须要咱们去修复错误。测试

咱们知道一个智能合约包含两部分: 代码逻辑和数据,而代码逻辑又是最容易出问题的部分, 如在实现以下合约时,因为手抖在写addTen()时,10写成了11。ui

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的话,只好从新部署合约,但是这时会有一个尴尬的问题,原来的合约已经有不少人使用,若是部署新的合约,老合约的数据将会丢失。设计

数据合约及控制合约

那么如何解决上面的问题了,一个解决方案是分离合约中的数据,使用一个单独的合约来存储数据(下文称数据合约),使用一个单独的合约写业务逻辑(下文称控制合约)。 咱们来看看代码如何实现。code

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来读写数据。经过这样的设计,能够在更新控制合约后保持数据合约不变,这样就不会丢失数据,也不用迁移数据。orm

读写控制

经过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;
    }
}

...

订阅个人小专栏可参看合约的完整代码。get

部署方法以下:

  1. 先部署DataContract合约
  2. 使用DataContract合约地址做为部署ControlContract合约的参数
  3. 用ControlContract合约地址做为参数调用DataContract合约的allowAccess方法。 若是须要更新控制合约(如修复了addTen)则从新执行第2-3步,同时对老的控制合约执行denyAccess()。

更多

当咱们在实现数据合约时,它包含的逻辑应该越少越好,而且应该是严格测试过的,由于一旦数据合约部署以后,就无法更改。 大多数状况下,和用户交互的是DApp, 所以当控制合约升级以后,须要升级DApp,使之关联新的控制合约。

尽管合约能够经过本文的方式升级,但咱们依然要谨慎升级,由于升级表示你能够重写逻辑,会下降用户对你的信任度。 本文介绍升级方法更多的是一种思路,实际项目中可能会对应多个控制合约及数据合约。

深刻浅出区块链 - 系统学习区块链,打造最好的区块链技术博客。

☛ 个人知识星球为各位解答区块链技术问题,欢迎加入讨论。

☛ 关注公众号“深刻浅出区块链技术”第一时间获取区块链技术信息。

相关文章
相关标签/搜索