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