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

     1  // Copyright 2019-2020 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 { GlobalConstantsV2 } from "./constantsV2.sol";
    26  import { IGovernedContract, GovernedContract } from "./GovernedContract.sol";
    27  import { IGovernedProxy } from "./IGovernedProxy.sol";
    28  import { IBlockReward } from "./IBlockReward.sol";
    29  import { IMasternodeRegistryV2 } from "./IMasternodeRegistryV2.sol";
    30  import { IMasternodeToken } from "./IMasternodeToken.sol";
    31  import { ITreasury } from "./ITreasury.sol";
    32  import { NonReentrant } from "./NonReentrant.sol";
    33  import { StorageBase }  from "./StorageBase.sol";
    34  import {
    35      MasternodeRegistryV1,
    36      StorageMasternodeRegistryV1
    37  } from "./MasternodeRegistryV1.sol";
    38  
    39  /**
    40   * MN-2: Genesis hardcoded version of MasternodeRegistry
    41   *
    42   * NOTE: it MUST NOT change after blockchain launch!
    43   */
    44  contract MasternodeRegistryV2 is
    45      GlobalConstants,
    46      GlobalConstantsV2,
    47      GovernedContract,
    48      IBlockReward,
    49      IMasternodeRegistryV2,
    50      NonReentrant
    51  {
    52      // MN-4 approximation logic:
    53      // - the target is 1000 hearbeats per hour
    54      // - {MN count} / 1000/3600 ~ {MN count} * 4
    55      uint constant internal TARGET_HEARTBEATS_COEF = 4;
    56  
    57      enum Config {
    58          RequireValidation,
    59          ValidationPeriods,
    60          CleanupPeriod,
    61          InitialEverCollateral,
    62          PaymentsPerBlock
    63      }
    64  
    65      // Data for migration
    66      //---------------------------------
    67      StorageMasternodeRegistryV1 public v1storage;
    68  
    69      IGovernedProxy public token_proxy;
    70      IGovernedProxy public treasury_proxy;
    71  
    72      uint public mn_announced;
    73  
    74      address public current_masternode;
    75      uint public current_payouts;
    76      uint public require_validation;
    77      uint public validation_periods;
    78      uint public cleanup_period;
    79      uint public payments_per_block;
    80      //---------------------------------
    81  
    82      // Not for migration
    83      struct Status {
    84          uint256 sw_features;
    85          uint next_heartbeat;
    86          uint inactive_since;
    87          uint validator_index;
    88          uint invalidations;
    89          uint seq_payouts;
    90          uint last_vote_epoch;
    91      }
    92  
    93      uint public mn_ever_collateral;
    94      uint public mn_active_collateral;
    95      uint public mn_announced_collateral;
    96  
    97      uint public mn_active;
    98      mapping(address => Status) public mn_status;
    99      address[] public validator_list;
   100      uint public last_block_number;
   101  
   102      uint public curr_validation_ends;
   103      uint public curr_validation_offset;
   104      //---------------------------------
   105  
   106      constructor(
   107          address _proxy,
   108          IGovernedProxy _token_proxy,
   109          IGovernedProxy _treasury_proxy,
   110          uint[5] memory _config
   111      )
   112          public
   113          GovernedContract(_proxy)
   114      {
   115          v1storage = new StorageMasternodeRegistryV1();
   116          token_proxy = _token_proxy;
   117          treasury_proxy = _treasury_proxy;
   118  
   119          require_validation = _config[uint(Config.RequireValidation)];
   120          validation_periods = _config[uint(Config.ValidationPeriods)];
   121          cleanup_period = _config[uint(Config.CleanupPeriod)];
   122          payments_per_block = _config[uint(Config.PaymentsPerBlock)];
   123  
   124          require(validation_periods <= require_validation, "Validations > Require");
   125  
   126          uint initial_ever_collateral = _config[uint(Config.InitialEverCollateral)];
   127          mn_ever_collateral = initial_ever_collateral;
   128          require(initial_ever_collateral >= MN_COLLATERAL_V2_MIN, "Initial collateral");
   129  
   130          _processValidationEpoch();
   131      }
   132  
   133      // IMasternodeRegistry
   134      //---------------------------------
   135  
   136      enum ValidationStatus {
   137          MNActive,
   138          MNCollaterIssue,
   139          MNNotActive,
   140          MNHeartbeat
   141      }
   142  
   143      uint constant internal GAS_RESERVE = 100000;
   144  
   145      // solium-disable security/no-block-members
   146  
   147      // Announcement
   148      //=================================
   149  
   150      function announce(address masternode, uint32 ipv4address, bytes32[2] calldata enode)
   151          external
   152          noReentry
   153      {
   154          address owner = _callerAddress();
   155  
   156          // Check collateral
   157          //---
   158          uint balance = _announce_checkbalance(owner);
   159  
   160          _announce(masternode, owner, balance, ipv4address, enode);
   161      }
   162  
   163      function _announce(
   164          address masternode,
   165          address owner,
   166          uint balance,
   167          uint32 ipv4address,
   168          bytes32[2] memory enode
   169      ) internal {
   170          StorageMasternodeRegistryV1 mn_storage = v1storage;
   171  
   172          // Cleanup & checks
   173          //---
   174          _announce_clear_old(mn_storage, owner);
   175          _announce_check_free(mn_storage, masternode);
   176          _announce_check_ipv4(ipv4address);
   177  
   178          // Insert into list
   179          //---
   180          (address next, address prev) = _announce_insert(mn_storage, masternode);
   181  
   182          // Save
   183          //---
   184          mn_storage.setMasternode(
   185              masternode,
   186              address(uint160(owner)),
   187              ipv4address,
   188              enode,
   189              balance,
   190              block.number,
   191              prev,
   192              next
   193          );
   194  
   195          Status storage mnstatus = mn_status[masternode];
   196          mnstatus.next_heartbeat = block.timestamp;
   197          mnstatus.seq_payouts = balance / MN_COLLATERAL_V2_MIN;
   198          ++mn_active;
   199          ++mn_announced;
   200  
   201          mn_active_collateral += balance;
   202          uint announced_collateral = mn_announced_collateral;
   203          announced_collateral += balance;
   204          mn_announced_collateral = announced_collateral;
   205  
   206          if (announced_collateral > mn_ever_collateral) {
   207              mn_ever_collateral = announced_collateral;
   208          }
   209  
   210          // Validator logic is de-coupled for easier changes
   211          //---
   212          mnstatus.validator_index = validator_list.length;
   213          validator_list.push(masternode);
   214  
   215          // Event
   216          //---
   217          emit Announced(masternode, owner, ipv4address, enode, balance);
   218      }
   219  
   220      function _announce_checkbalance(address owner) internal view returns(uint balance) {
   221          (balance,) = _getCollateralInfo(owner);
   222          require(balance >= MN_COLLATERAL_V2_MIN, "Invalid collateral");
   223      }
   224  
   225      function _announce_clear_old(StorageMasternodeRegistryV1 mn_storage, address owner) internal {
   226          address old_masternode = mn_storage.owner_masternodes(owner);
   227  
   228          // Regardless if it is re-announcement
   229          if (old_masternode != address(0)) {
   230              _denounce(old_masternode, owner);
   231          }
   232      }
   233  
   234      function _announce_check_free(StorageMasternodeRegistryV1 mn_storage, address masternode)
   235          internal view
   236      {
   237          // SECURITY: there is an option of seizing a foreign MN address at cost of collateral.
   238          //           The mitigation is regeneration of such address by a victim.
   239          //           MN should refuse to operate in such condition.
   240          StorageMasternodeRegistryV1.Info memory mninfo = _mnInfo(mn_storage, masternode);
   241          require(mninfo.owner == address(0), "Invalid owner");
   242      }
   243  
   244      function _announce_check_ipv4(uint32 ipv4address) internal pure {
   245          uint a = ipv4address & 0xFF000000;
   246          uint b = ipv4address & 0x00FF0000;
   247          uint c = ipv4address & 0x0000FF00;
   248          // solium-disable operator-whitespace
   249          require(
   250              // 127.0.0.0/8
   251              (a != (127 << 24)) &&
   252              // 10.0.0.0/8
   253              (a != (10 << 24)) &&
   254              // 192.168.0.0/16
   255              !((a == (192 << 24)) && (b == (168 << 16))) &&
   256              // 172.16.0.0/12
   257              !((a == (172 << 24)) && ((b & 0x00F00000) == (16 << 16))) &&
   258              // 0.0.0.0/8
   259              (a != 0) &&
   260              // 100.64.0.0/10
   261              !((a == (100 << 24)) && ((b & 0x00C00000) == (64 << 16))) &&
   262              // 169.254.0.0/16
   263              !((a == (169 << 24)) && (b == (254 << 16))) &&
   264              // 198.18.0.0/15
   265              !((a == (198 << 24)) && ((b & 0x00FE0000) == (18 << 16))) &&
   266              // 198.51.100.0/24
   267              !((a == (198 << 24)) && (b == (51 << 16)) && (c == (100 << 8))) &&
   268              // 203.0.113.0/24
   269              !((a == (203 << 24)) && (b == (0 << 16)) && (c == (113 << 8))) &&
   270              // 224.0.0.0/4
   271              ((a & 0xF0000000) != (224 << 24)) &&
   272              // 240.0.0.0/4
   273              ((a & 0xF0000000) != (240 << 24)) &&
   274              // 255.255.255.255/32
   275              (ipv4address != 0xFFFFFFFF),
   276              "Wrong IP");
   277          // solium-enable operator-whitespace
   278      }
   279  
   280      function _announce_insert(StorageMasternodeRegistryV1 mn_storage, address masternode)
   281          internal
   282          returns(address next, address prev)
   283      {
   284          // NOTE: always insert as the last - before the current one
   285          next = current_masternode;
   286  
   287          if (next != address(0)) {
   288              StorageMasternodeRegistryV1.Info memory nextinfo = _mnInfo(mn_storage, next);
   289  
   290              prev = nextinfo.prev;
   291  
   292              // Not effective for the second one, but reliable
   293              mn_storage.setMasternodePos(
   294                  nextinfo.prev,
   295                  false, address(0),
   296                  true, masternode
   297              );
   298              mn_storage.setMasternodePos(
   299                  next,
   300                  true, masternode,
   301                  false, address(0)
   302              );
   303          } else {
   304              // The first one
   305              current_masternode = masternode;
   306              current_payouts = 0;
   307              prev = masternode;
   308              next = masternode;
   309          }
   310      }
   311  
   312      //=================================
   313  
   314      function denounce(address masternode)
   315          external
   316          noReentry
   317      {
   318          _denounce(masternode, _callerAddress());
   319      }
   320  
   321      function _denounce(address masternode, address owner) internal {
   322          // Check masternode ownership, if already registered.
   323          //---
   324          StorageMasternodeRegistryV1 mn_storage = v1storage;
   325          StorageMasternodeRegistryV1.Info memory mninfo = _mnInfo(mn_storage, masternode);
   326  
   327          if (mninfo.owner == address(0)) {
   328              return;
   329          }
   330  
   331          require((owner == mninfo.owner), "Invalid owner");
   332  
   333          // Remove from list
   334          //---
   335          if (mninfo.next == masternode) {
   336              // the last one
   337              current_masternode = address(0);
   338          } else {
   339              if (current_masternode == masternode) {
   340                  current_masternode = mninfo.next;
   341                  current_payouts = 0;
   342              }
   343  
   344              mn_storage.setMasternodePos(
   345                  mninfo.prev,
   346                  false, address(0),
   347                  true, mninfo.next
   348              );
   349              mn_storage.setMasternodePos(
   350                  mninfo.next,
   351                  true, mninfo.prev,
   352                  false, address(0)
   353              );
   354          }
   355  
   356          // Delete
   357          //---
   358  
   359          mn_announced_collateral -= mninfo.collateral;
   360  
   361          if (mn_status[masternode].seq_payouts > 0) {
   362              _deactive_common(masternode, mninfo.collateral);
   363          }
   364  
   365          delete mn_status[masternode];
   366  
   367          mn_storage.deleteMasternode(masternode);
   368          --mn_announced;
   369  
   370          //---
   371          emit Denounced(masternode, mninfo.owner);
   372      }
   373  
   374      function _deactive_common(address masternode, uint collateral) internal {
   375          // Remove from validators
   376          address last_validator = validator_list[validator_list.length - 1];
   377          uint validator_index = mn_status[masternode].validator_index;
   378  
   379          mn_status[last_validator].validator_index = validator_index;
   380          validator_list[validator_index] = last_validator;
   381          validator_list.pop();
   382  
   383          //--
   384          --mn_active;
   385          mn_active_collateral -= collateral;
   386      }
   387  
   388      function heartbeat(uint block_number, bytes32 block_hash, uint sw_features)
   389          external
   390          noReentry
   391      {
   392          require((block.number - block_number - 1) <= MN_HEARTBEAT_PAST_BLOCKS, "Too old block");
   393          require(blockhash(block_number) == block_hash, "Block mismatch");
   394  
   395          address payable masternode = _callerAddress();
   396  
   397          Status storage s = mn_status[masternode];
   398  
   399          require(_isActive(masternode, s), "Not active");
   400  
   401          require(s.next_heartbeat <= block.timestamp, "Too early");
   402  
   403          s.next_heartbeat = block.timestamp + _newHeartbeatInterval();
   404          s.sw_features = sw_features;
   405      }
   406  
   407      function _newHeartbeatInterval() internal view returns(uint delay) {
   408          delay = mn_active * TARGET_HEARTBEATS_COEF;
   409  
   410          if (delay < MN_HEARTBEAT_INTERVAL_MIN) {
   411              delay = MN_HEARTBEAT_INTERVAL_MIN;
   412          }
   413      }
   414  
   415      function invalidate(address masternode)
   416          external
   417          noReentry
   418      {
   419          address caller = _callerAddress();
   420          require(caller != masternode, "Invalidation for self");
   421  
   422          uint vote_epoch = curr_validation_ends;
   423  
   424          //---
   425          Status storage cs = mn_status[caller];
   426          require(_isActive(caller, cs), "Not active caller");
   427          require(cs.last_vote_epoch < vote_epoch, "Already invalidated");
   428          require(validationTarget(caller) == masternode, "Invalid target");
   429  
   430          //---
   431          Status storage s = mn_status[masternode];
   432  
   433          require(_isActive(masternode, s), "Not active target");
   434  
   435          //---
   436          cs.last_vote_epoch = vote_epoch;
   437          s.invalidations++;
   438  
   439          emit Invalidation(masternode, caller);
   440      }
   441  
   442      function validationTarget(address masternode) public view returns(address target) {
   443          uint total = validator_list.length;
   444          uint offset = curr_validation_offset;
   445  
   446          // NOTE: a valid case when MN count changes
   447          if (offset == total) {
   448              offset += 1;
   449          }
   450  
   451          uint target_index = mn_status[masternode].validator_index;
   452          target_index = (target_index + offset) % total;
   453  
   454          return validator_list[target_index];
   455      }
   456  
   457      function _processValidationEpoch() internal {
   458          if (block.number >= curr_validation_ends) {
   459              uint total = validator_list.length;
   460              uint vperiods = validation_periods;
   461  
   462              // means validation is not enabled
   463              if (total < vperiods) {
   464                  return;
   465              }
   466  
   467              uint vperiod = total / vperiods;
   468  
   469              // NOTE: vperiods serves as both validation period count per cycle and as mininal block count.
   470              if (vperiod < vperiods ) {
   471                  vperiod = vperiods;
   472              }
   473  
   474              curr_validation_ends = block.number + vperiod;
   475              curr_validation_offset = (uint256(blockhash(block.number - 1)) % (total - 1)) + 1;
   476          }
   477      }
   478  
   479      function isActive(address masternode) external view returns(bool) {
   480          return _isActive(masternode, mn_status[masternode]);
   481      }
   482  
   483      //===
   484  
   485      function _isActive(address masternode, Status storage mnstatus) internal view returns(bool) {
   486          StorageMasternodeRegistryV1.Info memory mninfo = _mnInfo(v1storage, masternode);
   487          return _checkStatus(mnstatus, mninfo) == ValidationStatus.MNActive;
   488      }
   489  
   490      function _checkStatus(
   491          Status storage mnstatus,
   492          StorageMasternodeRegistryV1.Info memory mninfo
   493      )
   494          internal view
   495          returns(ValidationStatus)
   496      {
   497          (uint balance, uint last_block) = _getCollateralInfo(mninfo.owner);
   498          return _checkStatus(mnstatus, mninfo, balance, last_block);
   499      }
   500  
   501      function _checkStatus(
   502          Status storage mnstatus,
   503          StorageMasternodeRegistryV1.Info memory mninfo,
   504          uint balance,
   505          uint last_block
   506      )
   507          internal view
   508          returns(ValidationStatus)
   509      {
   510          if (mnstatus.seq_payouts == 0) {
   511              return ValidationStatus.MNNotActive;
   512          }
   513  
   514          if (block.timestamp > (mnstatus.next_heartbeat + MN_HEARTBEAT_INTERVAL_MAX)) {
   515              return ValidationStatus.MNHeartbeat;
   516          }
   517  
   518          if (balance != mninfo.collateral) {
   519              return ValidationStatus.MNCollaterIssue;
   520          }
   521  
   522          if (last_block > mninfo.announced_block) {
   523              return ValidationStatus.MNCollaterIssue;
   524          }
   525  
   526          return ValidationStatus.MNActive;
   527      }
   528  
   529      function _getCollateralInfo(address owner)
   530          internal view
   531          returns(
   532              uint balance,
   533              uint last_block
   534          )
   535      {
   536          (balance, last_block) = IMasternodeToken(address(token_proxy.impl())).balanceInfo(owner);
   537      }
   538  
   539      //===
   540  
   541      function count() external view
   542          returns(
   543              uint active, uint total,
   544              uint active_collateral, uint total_collateral,
   545              uint max_of_all_times
   546          )
   547      {
   548          active = mn_active;
   549          total = mn_announced;
   550          active_collateral = mn_active_collateral;
   551          total_collateral = mn_announced_collateral;
   552          max_of_all_times = mn_ever_collateral;
   553      }
   554  
   555      //===
   556      function info(address masternode) external view
   557          returns(
   558              address owner, uint32 ipv4address, bytes32[2] memory enode,
   559              uint collateral, uint announced_block, uint sw_features
   560          )
   561      {
   562          StorageMasternodeRegistryV1.Info memory mninfo = _mnInfo(v1storage, masternode);
   563          require(mninfo.owner != address(0), "Unknown masternode");
   564          owner = mninfo.owner;
   565          ipv4address = mninfo.ipv4address;
   566          enode = [ mninfo.enode_0, mninfo.enode_1 ];
   567          collateral = mninfo.collateral;
   568          announced_block = mninfo.announced_block;
   569  
   570          sw_features = mn_status[masternode].sw_features;
   571      }
   572  
   573      function ownerInfo(address owner) external view
   574          returns(
   575              address masternode, uint32 ipv4address, bytes32[2] memory enode,
   576              uint collateral, uint announced_block, uint sw_features
   577          )
   578      {
   579          StorageMasternodeRegistryV1 mnstorage = v1storage;
   580  
   581          masternode = mnstorage.owner_masternodes(owner);
   582          require(masternode != address(0), "Unknown owner");
   583  
   584          StorageMasternodeRegistryV1.Info memory mninfo = _mnInfo(mnstorage, masternode);
   585          masternode = masternode;
   586          ipv4address = mninfo.ipv4address;
   587          enode = [ mninfo.enode_0, mninfo.enode_1 ];
   588          collateral = mninfo.collateral;
   589          announced_block = mninfo.announced_block;
   590  
   591          sw_features = mn_status[masternode].sw_features;
   592      }
   593  
   594      function _mnInfo(
   595          StorageMasternodeRegistryV1 v1info,
   596          address masternode
   597      )
   598          internal view
   599          returns (StorageMasternodeRegistryV1.Info memory mninfo)
   600      {
   601          // NOTE: no ABIv2 encoding is enabled
   602          (
   603              mninfo.announced_block,
   604              mninfo.collateral,
   605              mninfo.enode_0,
   606              mninfo.enode_1,
   607              mninfo.owner,
   608              mninfo.prev,
   609              mninfo.next,
   610              mninfo.ipv4address
   611          ) = v1info.masternodes(masternode);
   612      }
   613  
   614      //===
   615  
   616      function onCollateralUpdate(address owner)
   617          external
   618          noReentry
   619      {
   620          // SECURITY: this is a callback for MasternodeToken, but
   621          //           it must be safe to be called by ANYONE.
   622  
   623          StorageMasternodeRegistryV1 mn_storage = v1storage;
   624          address masternode = mn_storage.owner_masternodes(owner);
   625  
   626          if (masternode == address(0)) {
   627              return;
   628          }
   629  
   630          (uint balance, uint last_block) = _getCollateralInfo(owner);
   631  
   632          StorageMasternodeRegistryV1.Info memory mninfo = _mnInfo(v1storage, masternode);
   633          ValidationStatus check = _checkStatus(mn_status[masternode], mninfo, balance, last_block);
   634  
   635          if (check == ValidationStatus.MNCollaterIssue) {
   636              // Re-announce, if there is collateral left.
   637              if (balance >= MN_COLLATERAL_V2_MIN) {
   638                  uint32 ipv4address = mninfo.ipv4address;
   639                  bytes32[2] memory enode = [mninfo.enode_0, mninfo.enode_1];
   640  
   641                  _announce(masternode, owner, balance, ipv4address, enode);
   642              } else {
   643                  _denounce(masternode, owner);
   644              }
   645          }
   646      }
   647  
   648      function enumerate()
   649          external view
   650          returns(address[] memory masternodes)
   651      {
   652          // NOTE: it should be OK for 0
   653          masternodes = new address[](mn_announced);
   654          address curr_mn = current_masternode;
   655  
   656          if (curr_mn == address(0)) {
   657              return masternodes;
   658          }
   659  
   660          address next = curr_mn;
   661          StorageMasternodeRegistryV1.Info memory mninfo;
   662          uint i = 0;
   663  
   664          do {
   665              masternodes[i] = next;
   666              mninfo = _mnInfo(v1storage, next);
   667              next = mninfo.next;
   668              ++i;
   669          } while (next != curr_mn);
   670      }
   671  
   672      function enumerateActive()
   673          external view
   674          returns(address[] memory masternodes)
   675      {
   676          // NOTE: this API is targeted at fast consensus execution
   677          masternodes = new address[](mn_active);
   678  
   679          for (uint i = 0; i < masternodes.length; ++i) {
   680              masternodes[i] = validator_list[i];
   681          }
   682      }
   683  
   684      // IMasternodeRegistryV2
   685      //---------------------------------
   686      function collateralLimits() external pure returns (uint min, uint max) {
   687          min = MN_COLLATERAL_V2_MIN;
   688          max = MN_COLLATERAL_MAX;
   689      }
   690  
   691      function canHeartbeat(address masternode) external view returns(bool can_heartbeat) {
   692          Status storage s = mn_status[masternode];
   693  
   694          return _isActive(masternode, s) && (s.next_heartbeat <= block.timestamp);
   695      }
   696  
   697      function canInvalidate(address masternode) external view returns(bool can_invalidate) {
   698          Status storage s = mn_status[masternode];
   699          return (
   700              _isActive(masternode, s) &&
   701              (s.last_vote_epoch < curr_validation_ends) &&
   702              (validationTarget(masternode) != masternode)
   703          );
   704      }
   705  
   706      //---------------------------------
   707  
   708      // IGovernedContract
   709      //---------------------------------
   710      function _migrate(IGovernedContract _oldImpl) internal {
   711          // Dispose
   712          v1storage.kill();
   713  
   714          MasternodeRegistryV1 oldinstance = MasternodeRegistryV1(address(_oldImpl));
   715          v1storage = oldinstance.v1storage();
   716  
   717          // Migration data
   718          mn_announced = oldinstance.mn_announced();
   719          current_masternode = oldinstance.current_masternode();
   720          current_payouts = oldinstance.current_payouts();
   721  
   722          // Other data
   723          mn_ever_collateral = oldinstance.mn_ever_collateral();
   724          mn_active_collateral = oldinstance.mn_active_collateral();
   725          mn_announced_collateral = oldinstance.mn_announced_collateral();
   726          mn_active = oldinstance.mn_active();
   727          address[] memory old_list = oldinstance.enumerate();
   728          last_block_number = block.number;
   729  
   730          // Restore the mn status information.
   731          // NOTE: this may be a serious gas consumption problem due to
   732          //       open limit.
   733          for (uint i = old_list.length; i-- > 0;) {
   734              address mn = old_list[i];
   735  
   736              Status memory status;
   737              (
   738                  status.sw_features,
   739                  ,
   740                  status.inactive_since,
   741                  ,
   742                  ,
   743                  status.invalidations,
   744                  status.seq_payouts,
   745                  status.last_vote_epoch
   746              ) = oldinstance.mn_status(mn);
   747  
   748              status.next_heartbeat = block.timestamp + (i * TARGET_HEARTBEATS_COEF);
   749              status.validator_index = validator_list.length;
   750              validator_list.push(mn);
   751  
   752              mn_status[mn] = status;
   753          }
   754  
   755          _processValidationEpoch();
   756      }
   757  
   758      function _destroy(IGovernedContract _newImpl) internal {
   759          v1storage.setOwner(_newImpl);
   760      }
   761  
   762      // IBlockReward
   763      //---------------------------------
   764      function reward()
   765          external payable
   766          noReentry
   767      {
   768          // NOTE: ensure to move of remaining from the previous times to Treasury
   769          //---
   770          uint diff = address(this).balance - msg.value;
   771  
   772          if (int(diff) > 0) {
   773              IBlockReward treasury = IBlockReward(address(treasury_proxy.impl()));
   774              treasury.reward.value(diff)();
   775          }
   776  
   777          //---
   778          // SECURITY: do processing only when reward is exactly as expected
   779          if (msg.value == REWARD_MASTERNODE_V1) {
   780              // SECURITY: this check is essential against Masternode skip attacks!
   781              require(last_block_number < block.number, "Call outside of governance!");
   782              last_block_number = block.number;
   783  
   784              // Update validation offset if needed
   785              _processValidationEpoch();
   786  
   787              // Safety checks
   788              assert(msg.value == address(this).balance);
   789              uint fractions = payments_per_block;
   790              uint reward_part = REWARD_MASTERNODE_V1 / fractions;
   791  
   792              for (uint i = fractions; i > 0; --i) {
   793                  assert(gasleft() > GAS_RESERVE);
   794  
   795                  // solium-disable-next-line no-empty-blocks
   796                  while ((gasleft() > GAS_RESERVE) && !_reward(reward_part)) {}
   797              }
   798          }
   799      }
   800  
   801      function _reward(uint reward_part) internal returns(bool) {
   802          //---
   803          address masternode = current_masternode;
   804          uint payouts = current_payouts;
   805  
   806          if (masternode == address(0)) {
   807              return true;
   808          }
   809  
   810          StorageMasternodeRegistryV1.Info memory mninfo = _mnInfo(v1storage, masternode);
   811  
   812          Status storage mnstatus = mn_status[masternode];
   813          uint invalidations = mnstatus.invalidations;
   814          ++payouts;
   815  
   816          if (payouts < mnstatus.seq_payouts) {
   817              current_payouts = payouts;
   818          } else {
   819              mnstatus.invalidations = 0;
   820              current_masternode = mninfo.next;
   821              current_payouts = 0;
   822          }
   823  
   824          // Reward logic
   825          //---
   826          ValidationStatus status = _checkStatus(mnstatus, mninfo);
   827  
   828          if (status == ValidationStatus.MNActive) {
   829              // solium-disable security/no-send
   830              if (!_canReward(invalidations) ||
   831                  mninfo.owner.send(reward_part)
   832              ) {
   833                  return true;
   834              }
   835              // solium-enable security/no-send
   836          }
   837  
   838          // When not valid
   839          //---
   840          if (status == ValidationStatus.MNCollaterIssue) {
   841              // Immediate
   842              _denounce(masternode, mninfo.owner);
   843          } else if (mnstatus.seq_payouts > 0) {
   844              // Mark as inactive for later auto-cleanup
   845              mnstatus.seq_payouts = 0;
   846              mnstatus.inactive_since = block.timestamp;
   847              _deactive_common(masternode, mninfo.collateral);
   848              current_masternode = mninfo.next;
   849              current_payouts = 0;
   850  
   851              emit Deactivated(masternode);
   852          } else if ((block.timestamp - mnstatus.inactive_since) > cleanup_period) {
   853              // Auto-cleanup
   854              _denounce(masternode, mninfo.owner);
   855          }
   856  
   857          return false;
   858      }
   859  
   860      function _canReward(uint invalidations) internal view returns(bool) {
   861          if (mn_active < require_validation) {
   862              return true;
   863          }
   864  
   865          uint threshold = (validation_periods + 1) / 2;
   866  
   867          return (invalidations < threshold);
   868      }
   869  
   870      //===
   871  
   872      function getReward(uint _blockNumber)
   873          external view
   874          returns(uint amount)
   875      {
   876          ITreasury treasury = ITreasury(address(treasury_proxy.impl()));
   877  
   878          if ((_blockNumber > 0) && !treasury.isSuperblock(_blockNumber)) {
   879              amount = REWARD_MASTERNODE_V1;
   880          }
   881      }
   882  
   883      // Safety
   884      //---------------------------------
   885      function () external payable {
   886          revert("Not supported");
   887      }
   888  }