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  }