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 }