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 }