github.com/codingfuture/orig-energi3@v0.8.4/energi/contracts/src/GenericProposalV1.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 { GlobalConstants } from "./constants.sol"; 25 import { IProposal } from "./IProposal.sol"; 26 import { IGovernedProxy } from "./IGovernedProxy.sol"; 27 import { IMasternodeRegistry } from "./IMasternodeRegistry.sol"; 28 import { ITreasury } from "./ITreasury.sol"; 29 import { StorageBase } from "./StorageBase.sol"; 30 31 // solium-disable security/no-block-members 32 33 /** 34 * Genesis hardcoded version of GenericProposal V1 35 * 36 * NOTE: it MUST NOT change after blockchain launch! 37 */ 38 contract GenericProposalV1 is 39 GlobalConstants, 40 IProposal 41 { 42 IGovernedProxy public mnregistry_proxy; 43 44 address public parent; 45 46 //! Block of proposal creation to check Masternode eligibility 47 uint public created_block; 48 49 //! Deadline for voting 50 uint public deadline; 51 52 //! The one who initiated the voting 53 address payable public fee_payer; 54 55 //! Fee amount 56 uint public fee_amount; 57 58 //! Weight of approval votes 59 uint public accepted_weight; 60 61 //! Weight of rejection votes 62 uint public rejected_weight; 63 64 //! Total masternode weight at the moment of proposal creation 65 uint public total_weight; 66 67 //! Weight of votes when the result is consider eligible 68 uint public quorum_weight; 69 70 //! Weight of votes when the voting can finalize before deadline 71 uint public finish_weight; 72 73 //! Registry of votes masternodes (vote once only) 74 mapping(address => bool) public voted; 75 76 /** 77 * C-tor 78 * 79 * @param _mnregistry_proxy - IMasternodeRegistry proxy 80 * @param _quorum - 1..100 81 * @param _period - in seconds until deadline 82 * @param _feePayer - the proposal initiator 83 */ 84 constructor( 85 IGovernedProxy _mnregistry_proxy, 86 uint8 _quorum, 87 uint _period, 88 address payable _feePayer 89 ) public { 90 parent = msg.sender; 91 created_block = block.number; 92 93 mnregistry_proxy = _mnregistry_proxy; 94 deadline = block.timestamp + _period; 95 fee_payer = _feePayer; 96 97 ( 98 , 99 , 100 uint _total_weight, // active_collaterel 101 , // total_collateral 102 uint _ever_weight 103 ) = IMasternodeRegistry(address(_mnregistry_proxy.impl())).count(); 104 105 require(_ever_weight > 0, "Not ready for proposals"); 106 require(_total_weight >= (_ever_weight/2), "Active weight < 1/2 ever weight"); 107 require(_quorum >= QUORUM_MIN, "Quorum min"); 108 require(_quorum <= QUORUM_MAX, "Quorum max"); 109 110 total_weight = _total_weight; 111 quorum_weight = _total_weight * _quorum / QUORUM_MAX; 112 113 if (_quorum >= QUORUM_MAJORITY) { 114 finish_weight = quorum_weight; 115 } else { 116 finish_weight = _total_weight * QUORUM_MAJORITY / QUORUM_MAX; 117 } 118 119 require(quorum_weight > 0, "Quorum weight"); 120 require(finish_weight > 0, "Finish weight"); 121 } 122 123 /** 124 * Check if the proposal is considered accepted. 125 * NOTE: It can happen before the deadline. 126 */ 127 function isAccepted() public view returns(bool) { 128 // Before the deadline condition 129 if (accepted_weight >= finish_weight) { 130 return true; 131 } 132 133 // Ensure finish condition is reaches otherwise 134 if (!isFinished()) { 135 return false; 136 } 137 138 // Check quorum 139 if ((accepted_weight + rejected_weight) < quorum_weight) { 140 return false; 141 } 142 143 // Simply majority 144 return accepted_weight > rejected_weight; 145 } 146 147 /** 148 * Check finish condition 149 */ 150 function isFinished() public view returns(bool) { 151 return ( 152 (deadline <= block.timestamp) || 153 (accepted_weight >= finish_weight) || 154 (rejected_weight > finish_weight) 155 ); 156 } 157 158 function _voteCommon() internal returns(uint collateral) { 159 // NOTE: do not use isFinished() to allow to accept votes before the deadline 160 require(deadline > block.timestamp, "Finished"); 161 162 IMasternodeRegistry registry = IMasternodeRegistry(address(mnregistry_proxy.impl())); 163 address owner = msg.sender; 164 165 uint announced_block; 166 (,,, collateral, announced_block,) = registry.ownerInfo(owner); 167 require(announced_block < created_block, "Not eligible"); 168 require(!voted[owner], "Already voted"); 169 voted[owner] = true; 170 } 171 172 /** 173 * Check if particular MN owner can vote 174 */ 175 function canVote(address owner) external view returns(bool) { 176 IMasternodeRegistry registry = IMasternodeRegistry(address(mnregistry_proxy.impl())); 177 178 uint announced_block; 179 (,,,, announced_block,) = registry.ownerInfo(owner); 180 181 return ( 182 (deadline > block.timestamp) && 183 (announced_block < created_block) && 184 !voted[owner] 185 ); 186 } 187 188 /** 189 * Masternode Owner approval vote 190 */ 191 function voteAccept() external { 192 accepted_weight += _voteCommon(); 193 } 194 195 /** 196 * Masternode Owner rejection vote 197 */ 198 function voteReject() external { 199 rejected_weight += _voteCommon(); 200 } 201 202 /** 203 * Withdrawal from accepted proposal. 204 * NOTE: Usually for fee, but can be for budget as well. 205 */ 206 function withdraw() external { 207 // NOTE: anyone should be able to do that for cases when payer is a contract 208 require(isAccepted(), "Not accepted"); 209 fee_payer.transfer(address(this).balance); 210 } 211 212 /** 213 * Destruction via Governance logic. 214 */ 215 function destroy() external { 216 // NOTE: unfinished voting must get canceled 217 require(msg.sender == parent, "Only parent"); 218 selfdestruct(fee_payer); 219 } 220 221 /** 222 * Allow Treasury to collect the fee of rejected proposals. 223 */ 224 function collect() external { 225 require(isFinished() && !isAccepted(), "Not collectable"); 226 require(msg.sender == parent, "Only parent"); 227 228 IMasternodeRegistry registry = IMasternodeRegistry(address(mnregistry_proxy.impl())); 229 ITreasury treasury = ITreasury(address(registry.treasury_proxy().impl())); 230 231 treasury.contribute.value(address(this).balance)(); 232 } 233 234 /** 235 * Set fee amount by parent 236 */ 237 function setFee() external payable { 238 require(msg.sender == parent, "Only parent"); 239 // NOTE: make sure it correctly handles multiple calls 240 fee_amount += msg.value; 241 } 242 243 /** 244 * Only accept fee from the parent creating contract 245 */ 246 function () external payable { 247 revert("Not allowed"); 248 } 249 }