智能合约升级的基本思路以及具体简单demo实现

1.智能合约可升级性

智能合约一旦部署无法进行修改,常见方案

1.1 主从合约

Master-Slave contracts

部署一个主合约,以及其他合约,其中主合约负责存储所有其他合约的地址,并在需要时返回所需的地址。

优点:简单

缺点:不易进行合约资产转移到新合约

1.2永久存储合约

Eternal Storage contracts

逻辑合约和数据合约彼此分开。 数据合约是永久性的,不可升级。 逻辑合约可以根据需要多次升级,并将更改通知给数据合约。通常会和主从技术相结合。

缺点:数据合约不可更改、逻辑合约外部调用数据合约消耗gas

1.3 可升级存储代理合约

Upgradable Storage Proxy Contracts

代理合约以及逻辑合约,将继承同一存储合约。

11114017.png

代理合约将有一个回退函数,委托调用逻辑合约,从而实现逻辑合约在代理存储中进行更改。

1.4委托调用

EVM所提供的DELEGATECALL操作码,DELEGATECALL就像是一个普通的CALL 调用操作码,不同之处在于目标地址上的代码是在调用合约上下文中执行的,而原始调用的msg.sender以及msg.value将被保留。

分离逻辑和数据合约

逻辑合约通过setter更新数据,而数据合约只允许逻辑合约调用setter。这允许在保持数据不变的同时更换实现逻辑,从而实现完全可升级的系统。

通过引导用户使用新的逻辑合约(通过诸如ENS的解析器)并更新数据合约的权限来允许新的逻辑合约执行setter,就可以实现合约的更新。

键值对数据模型分离逻辑和数据合约

不使用最终期望数据结构(struct,mapping等)来定义合约数据模型,所有数据都被抽象化并存储在键值对中,然后使用一个标准的命名系统以及sha256散列算法用于查找数据值。

2.实现DEMO

storage 存储

implementionV1 原智能合约

implementionV2 新智能合约

//0.4.25
contract StorageStructure {
    address public implementation;
    address public owner;
    mapping (address => uint) internal points;
    uint internal totalPlayers;
}

contract Proxy is StorageStructure {
    
    //确保只有所有者可以运行这个函数
    modifier onlyOwner() {
        require (msg.sender == owner);
        _;
    }
    
   //设置管理者owner地址
    constructor() public {
        owner = msg.sender;
    }
    
   //更新实现合约地址
    function upgradeTo(address _newImplementation) 
        external onlyOwner 
    {
        require(implementation != _newImplementation);
        _setImplementation(_newImplementation);
    }
    
   //回调
    function () payable public {
        address impl = implementation;
        require(impl != address(0));
        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize)
            let result := delegatecall(gas, impl, ptr, calldatasize, 0, 0)
            let size := returndatasize
            returndatacopy(ptr, 0, size)
            
            switch result
            case 0 { revert(ptr, size) }
            default { return(ptr, size) }
        }
    }
    
    //设置当前实现地址
    function _setImplementation(address _newImp) internal {
        implementation = _newImp;
    }
}




contract ImplementationV1 is StorageStructure {modifier onlyOwner() {
        require (msg.sender == owner);
        _;
    }
 
    function addPlayer(address _player, uint _points) 
        public onlyOwner 
    {
        require (points[_player] == 0);
        points[_player] = _points;
    }
    function setPoints(address _player, uint _points) 
        public onlyOwner 
    {
        require (points[_player] != 0);
        points[_player] = _points;
    }
}



contract ImplementationV2 is ImplementationV1 {
 
    function addPlayer(address _player, uint _points) 
        public onlyOwner 
    {
        require (points[_player] == 0);
        points[_player] = _points;
        totalPlayers++;
    }

}
    
  • 依次部署 proxy合约、ImplementationV1合约

  • 调用proxy合约的upgradeTo(address)函数:

    address为部署后的implementionV1合约地址。

  • 重新部署 proxy合约,此时只需使用proxy合约即可

  • 合约升级: 重写并部署implementionV2合约 ,并将其合约地址,作为调用proxy合约的upgradeTo(address)参数

111.gif

相关文章链接

https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2

https://yos.io/2018/10/28/upgrading-solidity-smart-contracts/

Logo

为所有Web3兴趣爱好者提供学习成长、分享交流、生态实践、资源工具等服务,作为Anome Land原住民可不断优先享受各种福利,共同打造全球最大的Web3 UGC游戏平台。

更多推荐