github.com/codingfuture/orig-energi3@v0.8.4/energi/contracts/src/BlacklistRegistryV1.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 { IGovernedContract, GovernedContract } from "./GovernedContract.sol"; 26 import { IBlacklistRegistry, IBlacklistProposal } from "./IBlacklistRegistry.sol"; 27 import { IGovernedProxy } from "./IGovernedProxy.sol"; 28 import { ITreasury } from "./ITreasury.sol"; 29 import { Gen2Migration } from "./Gen2Migration.sol"; 30 import { GenericProposalV1 } from "./GenericProposalV1.sol"; 31 import { StorageBase } from "./StorageBase.sol"; 32 import { NonReentrant } from "./NonReentrant.sol"; 33 34 contract BlacklistProposalV1 is 35 GenericProposalV1, 36 IBlacklistProposal 37 { 38 constructor(IGovernedProxy _mnregistry_proxy, address payable fee_payer) 39 public 40 GenericProposalV1( 41 _mnregistry_proxy, 42 10, 43 1 weeks, 44 fee_payer 45 ) 46 // solium-disable-next-line no-empty-blocks 47 {} 48 49 function isObeyed() 50 external view 51 returns(bool) 52 { 53 if (isAccepted()) { 54 return true; 55 } 56 57 uint accepted = accepted_weight; 58 uint rejected = rejected_weight; 59 60 if ((accepted > (rejected*2)) && (accepted > MN_COLLATERAL_MAX)) { 61 return true; 62 } 63 64 return false; 65 } 66 } 67 68 /** 69 * A workaround for BlacklistRegistryV1 deploy-time gas consumption 70 */ 71 contract BlacklistV1ProposalCreator is 72 StorageBase 73 { 74 function create(IGovernedProxy mnregistry_proxy, address payable fee_payer) 75 external payable 76 requireOwner 77 returns(IBlacklistProposal) 78 { 79 BlacklistProposalV1 proposal = new BlacklistProposalV1( 80 mnregistry_proxy, 81 fee_payer 82 ); 83 84 proposal.setFee.value(msg.value)(); 85 return proposal; 86 } 87 } 88 89 90 /** 91 * Permanent storage of Blacklist Registry V1 data. 92 */ 93 contract StorageBlacklistRegistryV1 is 94 StorageBase 95 { 96 // NOTE: ABIEncoderV2 is not acceptable at the moment of development! 97 98 struct Info { 99 IBlacklistProposal enforce; 100 IBlacklistProposal revoke; 101 IBlacklistProposal drain; 102 uint index; 103 } 104 105 mapping(address => Info) public address_info; 106 address[] public address_list; 107 108 function setEnforce(address addr, IBlacklistProposal proposal) 109 external 110 requireOwner 111 { 112 Info storage item = address_info[addr]; 113 assert(address(item.enforce) == address(0)); 114 115 item.enforce = proposal; 116 item.index = address_list.length; 117 address_list.push(addr); 118 } 119 120 function setRevoke(address addr, IBlacklistProposal proposal) 121 external 122 requireOwner 123 { 124 Info storage item = address_info[addr]; 125 126 assert(address(item.enforce) != address(0)); 127 128 item.revoke = proposal; 129 } 130 131 function setDrain(address addr, IBlacklistProposal proposal) 132 external 133 requireOwner 134 { 135 Info storage item = address_info[addr]; 136 137 assert(address(item.enforce) != address(0)); 138 139 item.drain = proposal; 140 } 141 142 function remove(address addr) 143 external 144 requireOwner 145 { 146 Info storage item = address_info[addr]; 147 assert(address(item.enforce) != address(0)); 148 149 // Ensure re-ordered index is updated 150 address last = address_list[address_list.length - 1]; 151 address_info[last].index = item.index; 152 153 // Move the last into the gap, NOOP on on match 154 address_list[item.index] = last; 155 address_list.pop(); 156 157 delete address_info[addr]; 158 } 159 160 function addresses() 161 external view 162 returns(address[] memory result) 163 { 164 uint len = address_list.length; 165 result = new address[](len); 166 167 for (uint i = 0; i < len; ++i) { 168 result[i] = address_list[i]; 169 } 170 } 171 } 172 173 174 /** 175 * Genesis hardcoded version of BlacklistRegistry. 176 * 177 * NOTE: it MUST NOT change after blockchain launch! 178 */ 179 contract BlacklistRegistryV1 is 180 GovernedContract, 181 NonReentrant, 182 GlobalConstants, 183 IBlacklistRegistry 184 { 185 // Data for migration 186 //--------------------------------- 187 BlacklistV1ProposalCreator public proposal_creator; 188 StorageBlacklistRegistryV1 public v1storage; 189 IGovernedProxy public mnregistry_proxy; 190 Gen2Migration public migration; 191 ITreasury public compensation_fund; 192 address public EBI_signer; 193 //--------------------------------- 194 195 constructor( 196 address _proxy, 197 IGovernedProxy _mnregistry_proxy, 198 Gen2Migration _migration, 199 ITreasury _compensation_fund, 200 address _ebi_signer 201 ) 202 public GovernedContract(_proxy) 203 { 204 proposal_creator = new BlacklistV1ProposalCreator(); 205 v1storage = new StorageBlacklistRegistryV1(); 206 mnregistry_proxy = _mnregistry_proxy; 207 migration = _migration; 208 compensation_fund = _compensation_fund; 209 EBI_signer = _ebi_signer; 210 } 211 212 // IGovernedContract 213 //--------------------------------- 214 function _destroy(IGovernedContract _newImpl) internal { 215 v1storage.setOwner(_newImpl); 216 proposal_creator.kill(); 217 } 218 219 // IBlacklistRegistry 220 //--------------------------------- 221 function proposals(address addr) 222 external view 223 returns(IBlacklistProposal enforce, IBlacklistProposal revoke, IBlacklistProposal drain) 224 { 225 (enforce, revoke, drain,) = v1storage.address_info(addr); 226 } 227 228 function _createProposal() internal returns(IBlacklistProposal) { 229 // solium-disable-next-line security/no-low-level-calls 230 (bool s, bytes memory r) = address(proposal_creator).delegatecall( 231 abi.encodeWithSelector( 232 proposal_creator.create.selector, 233 mnregistry_proxy, _callerAddress()) 234 ); 235 require(s, string(r)); 236 return abi.decode(r, (IBlacklistProposal)); 237 } 238 239 // solium-disable-next-line security/no-assign-params 240 function _requireFee(uint fee) internal { 241 if (_callerAddress() == EBI_signer) { 242 fee = 0; 243 } 244 245 require(msg.value == fee, "Invalid fee"); 246 } 247 248 function propose(address addr) 249 external payable 250 noReentry 251 returns(IBlacklistProposal) 252 { 253 _requireFee(FEE_BLACKLIST_V1); 254 255 StorageBlacklistRegistryV1 store = v1storage; 256 (IBlacklistProposal enforce, IBlacklistProposal revoke, IBlacklistProposal drain,) = store.address_info(addr); 257 258 // Cleanup old 259 if (address(enforce) != address(0)) { 260 if (address(revoke) != address(0)) { 261 // assume enforced 262 if (revoke.isAccepted()) { 263 enforce.destroy(); 264 revoke.destroy(); 265 if (address(drain) != address(0)) { 266 drain.destroy(); 267 } 268 store.remove(addr); 269 } else if (revoke.isFinished()) { 270 revert("Already active (1)"); 271 } 272 } else if (enforce.isFinished() && !enforce.isAccepted()) { 273 enforce.collect(); 274 // See below 275 if (address(drain) != address(0)) { 276 drain.destroy(); 277 } 278 store.remove(addr); 279 } else { 280 revert("Already active (2)"); 281 } 282 } 283 284 // Create new 285 IBlacklistProposal proposal = _createProposal(); 286 287 store.setEnforce(addr, proposal); 288 289 emit BlacklistProposal(addr, proposal); 290 291 return proposal; 292 } 293 294 function proposeRevoke(address addr) 295 external payable 296 noReentry 297 returns(IBlacklistProposal) 298 { 299 _requireFee(FEE_BLACKLIST_REVOKE_V1); 300 301 StorageBlacklistRegistryV1 store = v1storage; 302 (IBlacklistProposal enforce, IBlacklistProposal revoke,,) = store.address_info(addr); 303 304 // Cleanup old 305 require(address(enforce) != address(0), "No need (1)"); 306 307 if (address(revoke) != address(0)) { 308 // assume enforced 309 if (!revoke.isFinished()) { 310 revert("Already active"); 311 } else if (!revoke.isAccepted()) { 312 revoke.collect(); 313 } 314 } else if (!enforce.isFinished()) { 315 revert("Not applicable"); 316 } else if (!enforce.isAccepted()) { 317 revert("No need (2)"); 318 } 319 320 // Create new 321 IBlacklistProposal proposal = _createProposal(); 322 323 store.setRevoke(addr, proposal); 324 325 emit WhitelistProposal(addr, proposal); 326 327 return proposal; 328 } 329 330 function proposeDrain(address addr) 331 external payable 332 noReentry 333 returns(IBlacklistProposal) 334 { 335 _requireFee(FEE_BLACKLIST_DRAIN_V1); 336 337 require(isBlacklisted(address(addr)), "Not blacklisted"); 338 339 StorageBlacklistRegistryV1 store = v1storage; 340 (,, IBlacklistProposal drain,) = store.address_info(addr); 341 342 if (address(drain) != address(0)) { 343 if (drain.isAccepted()) { 344 revert("Not need"); 345 } else if (drain.isFinished()) { 346 drain.collect(); 347 } else { 348 revert("Voting in progress"); 349 } 350 } 351 352 // Create new 353 IBlacklistProposal proposal = _createProposal(); 354 355 store.setDrain(addr, proposal); 356 357 emit DrainProposal(addr, proposal); 358 359 return proposal; 360 } 361 362 function isBlacklisted(address addr) 363 public view 364 returns(bool) 365 { 366 StorageBlacklistRegistryV1 store = v1storage; 367 (IBlacklistProposal enforce, IBlacklistProposal revoke,,) = store.address_info(addr); 368 369 if ((address(revoke) != address(0)) && revoke.isAccepted()) { 370 return false; 371 } 372 373 if ((address(enforce) != address(0)) && enforce.isObeyed()) { 374 return true; 375 } 376 377 return false; 378 } 379 380 function isDrainable(address addr) 381 public view 382 returns(bool) 383 { 384 (IBlacklistProposal enforce, IBlacklistProposal revoke, IBlacklistProposal drain,) = v1storage.address_info(addr); 385 386 if (address(enforce) == address(0)) { 387 return false; 388 } 389 390 if (!enforce.isAccepted()) { 391 return false; 392 } 393 394 if ((address(revoke) != address(0)) && revoke.isAccepted()) { 395 return false; 396 } 397 398 if (address(drain) == address(0)) { 399 return false; 400 } 401 402 return drain.isAccepted(); 403 } 404 405 function collect(address addr) 406 external 407 noReentry 408 { 409 StorageBlacklistRegistryV1 store = v1storage; 410 (IBlacklistProposal enforce, IBlacklistProposal revoke, IBlacklistProposal drain,) = store.address_info(addr); 411 412 require(address(enforce) != address(0), "Nothing to collect"); 413 require(enforce.isFinished(), "Enforce voting in progress"); 414 415 if (!enforce.isAccepted()) { 416 enforce.collect(); 417 store.remove(addr); 418 return; 419 } 420 421 if (address(drain) != address(0)) { 422 require(drain.isFinished(), "Drain voting in progress"); 423 424 if (drain.isAccepted()) { 425 revert("Account must be drained"); 426 } 427 428 drain.collect(); 429 store.setDrain(addr, IBlacklistProposal(address(0))); 430 return; 431 } 432 433 if (address(revoke) != address(0)) { 434 require(revoke.isFinished(), "Revoke voting in progress"); 435 436 if (revoke.isAccepted()) { 437 enforce.destroy(); 438 revoke.destroy(); 439 assert(address(drain) == address(0)); 440 store.remove(addr); 441 } else { 442 revoke.collect(); 443 store.setRevoke(addr, IBlacklistProposal(address(0))); 444 } 445 return; 446 } 447 448 revert("No proposals ready to collect"); 449 } 450 451 function drainMigration(uint item_id, bytes20 owner) 452 external 453 noReentry 454 { 455 require(isDrainable(address(owner)), "Not drainable"); 456 migration.blacklistClaim(item_id, owner); 457 _onDrain(address(owner)); 458 } 459 460 function enumerateAll() 461 external view 462 returns(address[] memory addresses) 463 { 464 return v1storage.addresses(); 465 } 466 467 function enumerateBlocked() 468 external view 469 returns(address[] memory addresses) 470 { 471 addresses = v1storage.addresses(); 472 473 for (uint i = addresses.length; i-- > 0;) { 474 if (!isBlacklisted(addresses[i])) { 475 addresses[i] = address(0); 476 } 477 } 478 } 479 480 function enumerateDrainable() 481 external view 482 returns(address[] memory addresses) 483 { 484 addresses = v1storage.addresses(); 485 486 for (uint i = addresses.length; i-- > 0;) { 487 if (!isDrainable(addresses[i])) { 488 addresses[i] = address(0); 489 } 490 } 491 } 492 493 function onDrain(address addr) 494 external 495 noReentry 496 { 497 // solium-disable-next-line security/no-tx-origin 498 require(tx.origin == proxy, "Not consensus"); 499 _onDrain(addr); 500 } 501 502 function _onDrain(address addr) internal { 503 StorageBlacklistRegistryV1 store = v1storage; 504 (IBlacklistProposal enforce, IBlacklistProposal revoke, IBlacklistProposal drain,) = store.address_info(addr); 505 506 if (address(enforce) != address(0)) { 507 enforce.destroy(); 508 509 if (address(revoke) != address(0)) { 510 revoke.destroy(); 511 } 512 513 if (address(drain) != address(0)) { 514 drain.destroy(); 515 } 516 517 store.remove(addr); 518 } 519 } 520 521 // Safety 522 //--------------------------------- 523 function () external payable { 524 revert("Not supported"); 525 } 526 }