實現可升級智能合約

智能合約升級的基本思路以及具體簡單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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章