github.com/codingfuture/orig-energi3@v0.8.4/energi/contracts/src/GovernedProxy.sol (about)

     1  // Copyright 2019 The Energi Core Authors
     2  // This file is part of Energi Core.
     3  //
     4  // Energi Core is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // Energi Core is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with Energi Core. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Energi Governance system is the fundamental part of Energi Core.
    18  
    19  // NOTE: It's not allowed to change the compiler due to byte-to-byte
    20  //       match requirement.
    21  pragma solidity 0.5.16;
    22  //pragma experimental SMTChecker;
    23  
    24  import { IGovernedContract } from "./IGovernedContract.sol";
    25  import { IGovernedProxy } from "./IGovernedProxy.sol";
    26  import { IUpgradeProposal } from "./IUpgradeProposal.sol";
    27  import { ISporkRegistry } from "./ISporkRegistry.sol";
    28  import { NonReentrant } from "./NonReentrant.sol";
    29  
    30  /**
    31   * SC-9: This contract has no chance of being updated. It must be stupid simple.
    32   *
    33   * If another upgrade logic is required in the future - it can be done as proxy stage II.
    34   */
    35  contract GovernedProxy is
    36      IGovernedContract,
    37      IGovernedProxy,
    38      NonReentrant
    39  {
    40      modifier senderOrigin {
    41          // Internal calls are expected to use impl directly.
    42          // That's due to use of call() instead of delegatecall() on purpose.
    43          // solium-disable-next-line security/no-tx-origin
    44          require(tx.origin == msg.sender, "Only direct calls are allowed!");
    45          _;
    46      }
    47  
    48      IGovernedContract public impl;
    49      IGovernedProxy public spork_proxy;
    50      mapping(address => IGovernedContract) public upgrade_proposals;
    51      IUpgradeProposal[] public upgrade_proposal_list;
    52  
    53      constructor(IGovernedContract _impl, IGovernedProxy _sporkProxy) public {
    54          impl = _impl;
    55          spork_proxy = _sporkProxy;
    56      }
    57  
    58      /**
    59       * Pre-create a new contract first.
    60       * Then propose upgrade based on that.
    61       */
    62      function proposeUpgrade(IGovernedContract _newImpl, uint _period)
    63          external payable
    64          senderOrigin
    65          noReentry
    66          returns(IUpgradeProposal)
    67      {
    68          require(_newImpl != impl, "Already active!");
    69          require(_newImpl.proxy() == address(this), "Wrong proxy!");
    70  
    71          ISporkRegistry spork_reg = ISporkRegistry(address(spork_proxy.impl()));
    72          IUpgradeProposal proposal = spork_reg.createUpgradeProposal.value(msg.value)(_newImpl, _period, msg.sender);
    73  
    74          upgrade_proposals[address(proposal)] = _newImpl;
    75          upgrade_proposal_list.push(proposal);
    76  
    77          emit UpgradeProposal(_newImpl, proposal);
    78  
    79          return proposal;
    80      }
    81  
    82      /**
    83       * Once proposal is accepted, anyone can activate that.
    84       */
    85      function upgrade(IUpgradeProposal _proposal)
    86          external
    87          noReentry
    88      {
    89          IGovernedContract new_impl = upgrade_proposals[address(_proposal)];
    90          require(new_impl != impl, "Already active!"); // in case it changes in the flight
    91          require(address(new_impl) != address(0), "Not registered!");
    92          require(_proposal.isAccepted(), "Not accepted!");
    93  
    94          IGovernedContract old_impl = impl;
    95  
    96          new_impl.migrate(old_impl);
    97          impl = new_impl;
    98          old_impl.destroy(new_impl);
    99  
   100          // SECURITY: prevent downgrade attack
   101          _cleanupProposal(_proposal);
   102  
   103          // Return fee ASAP
   104          _proposal.destroy();
   105  
   106          emit Upgraded(new_impl, _proposal);
   107      }
   108  
   109      /**
   110       * Map proposal to implementation
   111       */
   112      function upgradeProposalImpl(IUpgradeProposal _proposal)
   113          external view
   114          returns(IGovernedContract new_impl)
   115      {
   116          new_impl = upgrade_proposals[address(_proposal)];
   117      }
   118  
   119      /**
   120       * Lists all available upgrades
   121       */
   122      function listUpgradeProposals()
   123          external view
   124          returns(IUpgradeProposal[] memory proposals)
   125      {
   126          uint len = upgrade_proposal_list.length;
   127          proposals = new IUpgradeProposal[](len);
   128  
   129          for (uint i = 0; i < len; ++i) {
   130              proposals[i] = upgrade_proposal_list[i];
   131          }
   132  
   133          return proposals;
   134      }
   135  
   136      /**
   137       * Once proposal is reject, anyone can start collect procedure.
   138       */
   139      function collectUpgradeProposal(IUpgradeProposal _proposal)
   140          external
   141          noReentry
   142      {
   143          IGovernedContract new_impl = upgrade_proposals[address(_proposal)];
   144          require(address(new_impl) != address(0), "Not registered!");
   145          _proposal.collect();
   146          delete upgrade_proposals[address(_proposal)];
   147  
   148          _cleanupProposal(_proposal);
   149      }
   150  
   151      function _cleanupProposal(IUpgradeProposal _proposal) internal {
   152          delete upgrade_proposals[address(_proposal)];
   153  
   154          uint len = upgrade_proposal_list.length;
   155          for (uint i = 0; i < len; ++i) {
   156              if (upgrade_proposal_list[i] == _proposal) {
   157                  upgrade_proposal_list[i] = upgrade_proposal_list[len - 1];
   158                  upgrade_proposal_list.pop();
   159                  break;
   160              }
   161          }
   162      }
   163  
   164      /**
   165       * Related to above
   166       */
   167      function proxy() external returns (address) {
   168          return address(this);
   169      }
   170  
   171      /**
   172       * SECURITY: prevent on-behalf-of calls
   173       */
   174      function migrate(IGovernedContract) external {
   175          revert("Good try");
   176      }
   177  
   178      /**
   179       * SECURITY: prevent on-behalf-of calls
   180       */
   181      function destroy(IGovernedContract) external {
   182          revert("Good try");
   183      }
   184  
   185      /**
   186       * Proxy all other calls to implementation.
   187       */
   188      function ()
   189          external payable
   190          senderOrigin
   191      {
   192          // SECURITY: senderOrigin() modifier is mandatory
   193          IGovernedContract impl_m = impl;
   194  
   195          // solium-disable-next-line security/no-inline-assembly
   196          assembly {
   197              let ptr := mload(0x40)
   198              calldatacopy(ptr, 0, calldatasize)
   199  
   200              let res := call(sub(gas, 10000), impl_m, callvalue, ptr, calldatasize, 0, 0)
   201              // NOTE: returndatasize should allow repeatable calls
   202              //       what should save one opcode.
   203              returndatacopy(ptr, 0, returndatasize)
   204  
   205              switch res
   206              case 0 {
   207                  revert(ptr, returndatasize)
   208              }
   209              default {
   210                  return(ptr, returndatasize)
   211              }
   212          }
   213      }
   214  }