github.com/klaytn/klaytn@v1.12.1/contracts/cnstaking/CnStakingContract.sol (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser 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  // The klaytn library 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 Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  pragma solidity 0.4.24;
    18  import "./SafeMath.sol";
    19  
    20  contract CnStakingContract {
    21      using SafeMath for uint256;
    22      /*
    23       *  Events
    24       */
    25      event DeployContract(string contractType, address contractValidator, address nodeId, address rewardAddress, address[] cnAdminList, uint256 requirement, uint256[] unlockTime, uint256[] unlockAmount);
    26      event ReviewInitialConditions(address indexed from);
    27      event CompleteReviewInitialConditions();
    28      event DepositLockupStakingAndInit(address from, uint256 value);
    29  
    30      event SubmitRequest(uint256 indexed id, address indexed from, Functions functionId, bytes32 firstArg, bytes32 secondArg, bytes32 thirdArg);
    31      event ConfirmRequest(uint256 indexed id, address indexed from, Functions functionId, bytes32 firstArg, bytes32 secondArg, bytes32 thirdArg, address[] confirmers);
    32      event RevokeConfirmation(uint256 indexed id, address indexed from, Functions functionId, bytes32 firstArg, bytes32 secondArg, bytes32 thirdArg, address[] confirmers);
    33      event CancelRequest(uint256 indexed id, address indexed from, Functions functionId, bytes32 firstArg, bytes32 secondArg, bytes32 thirdArg);
    34      event ExecuteRequestSuccess(uint256 indexed id, address indexed from, Functions functionId, bytes32 firstArg, bytes32 secondArg, bytes32 thirdArg);
    35      event ExecuteRequestFailure(uint256 indexed id, address indexed from, Functions functionId, bytes32 firstArg, bytes32 secondArg, bytes32 thirdArg);
    36      event ClearRequest();
    37  
    38      event AddAdmin (address indexed admin);
    39      event DeleteAdmin(address indexed admin);
    40      event UpdateRequirement(uint256 requirement);
    41      event WithdrawLockupStaking(address indexed to, uint256 value);
    42      event ApproveStakingWithdrawal(uint256 approvedWithdrawalId, address to, uint256 value, uint256 withdrawableFrom);
    43      event CancelApprovedStakingWithdrawal(uint256 approvedWithdrawalId, address to, uint256 value);
    44      event UpdateRewardAddress(address rewardAddress);
    45  
    46      event StakeKlay(address from, uint256 value);
    47      event WithdrawApprovedStaking(uint256 approvedWithdrawalId, address to, uint256 value);
    48  
    49      //AddressBook event
    50      event ReviseRewardAddress(address cnNodeId, address prevRewardAddress, address curRewardAddress);
    51  
    52  
    53      /*
    54       *  Constants
    55       */
    56      uint256 constant public MAX_ADMIN = 50;
    57      string constant public CONTRACT_TYPE = "CnStakingContract";
    58      uint256 constant public VERSION = 1;
    59      address constant public ADDRESS_BOOK_ADDRESS = 0x0000000000000000000000000000000000000400;
    60      uint256 constant public ONE_WEEK = 1 weeks;
    61  
    62  
    63      /*
    64       *  Enums
    65       */
    66      enum RequestState { Unknown, NotConfirmed, Executed, ExecutionFailed, Canceled }
    67      enum Functions { Unknown, AddAdmin, DeleteAdmin, UpdateRequirement, ClearRequest, WithdrawLockupStaking, ApproveStakingWithdrawal, CancelApprovedStakingWithdrawal, UpdateRewardAddress }
    68      enum WithdrawalStakingState { Unknown, Transferred, Canceled}
    69  
    70      /*
    71       *  Storage
    72       */
    73      address[] private adminList;
    74      uint256 public requirement;
    75      mapping (address => bool) private isAdmin;
    76      uint256 public lastClearedId;
    77  
    78      uint256 public requestCount;
    79      mapping(uint256 => Request) private requestMap;
    80      struct Request {
    81          Functions functionId;
    82          bytes32 firstArg;
    83          bytes32 secondArg;
    84          bytes32 thirdArg;
    85          address requestProposer;
    86          address[] confirmers;
    87          RequestState state;
    88      }
    89  
    90      address public contractValidator;
    91      bool public isInitialized;
    92      struct LockupConditions {
    93          uint256[] unlockTime;
    94          uint256[] unlockAmount;
    95          bool allReviewed;
    96          uint256 reviewedCount;
    97          mapping(address => bool) reviewedAdmin;
    98      }
    99      LockupConditions public lockupConditions;
   100      uint256 public initialLockupStaking;
   101      uint256 public remainingLockupStaking;
   102      address public nodeId;
   103      address public rewardAddress;
   104  
   105  
   106      uint256 public staking;
   107      uint256 public withdrawalRequestCount;
   108      mapping(uint256 => WithdrawalRequest) private withdrawalRequestMap;
   109      struct WithdrawalRequest {
   110          address to;
   111          uint256 value;
   112          uint256 withdrawableFrom;
   113          WithdrawalStakingState state;
   114      }
   115  
   116  
   117      /*
   118       *  Modifiers
   119       */
   120      modifier onlyMultisigTx() {
   121          require(msg.sender == address(this), "Not a multisig-transaction.");
   122          _;
   123      }
   124  
   125      modifier onlyAdmin(address _admin) {
   126          require(isAdmin[_admin], "Address is not admin.");
   127          _;
   128      }
   129  
   130      modifier adminDoesNotExist(address _admin) {
   131          require(!isAdmin[_admin], "Admin already exists.");
   132          _;
   133      }
   134  
   135      modifier notNull(address _address) {
   136          require(_address != 0, "Address is null");
   137          _;
   138      }
   139  
   140      modifier notConfirmedRequest(uint256 _id) {
   141          require(requestMap[_id].state == RequestState.NotConfirmed, "Must be at not-confirmed state.");
   142          _;
   143      }
   144  
   145      modifier validRequirement(uint256 _adminCount, uint256 _requirement) {
   146          require(_adminCount <= MAX_ADMIN
   147              && _requirement <= _adminCount
   148              && _requirement != 0
   149              && _adminCount != 0, "Invalid requirement.");
   150          _;
   151      }
   152  
   153      modifier beforeInit() {
   154          require(isInitialized == false, "Contract has been initialized.");
   155          _;
   156      }
   157  
   158      modifier afterInit() {
   159          require(isInitialized == true, "Contract is not initialized.");
   160          _;
   161      }
   162  
   163  
   164      /*
   165       *  Constructor
   166       */
   167  
   168      constructor(address _contractValidator, address _nodeId, address _rewardAddress, address[] _cnAdminlist, uint256 _requirement, uint256[] _unlockTime, uint256[] _unlockAmount) public
   169      validRequirement(_cnAdminlist.length, _requirement) 
   170      notNull(_nodeId) 
   171      notNull(_rewardAddress) {
   172  
   173          require(_contractValidator != 0, "Validator is null.");
   174          isAdmin[_contractValidator] = true;
   175          for(uint256 i = 0; i < _cnAdminlist.length; i++) {
   176              require(!isAdmin[_cnAdminlist[i]] && _cnAdminlist[i] != 0, "Address is null or not unique.");
   177              isAdmin[_cnAdminlist[i]] = true;
   178          }
   179  
   180  
   181          require(_unlockTime.length != 0 && _unlockAmount.length != 0 && _unlockTime.length == _unlockAmount.length, "Invalid unlock time and amount.");
   182          uint256 unlockTime = now;
   183  
   184          for (i = 0; i < _unlockAmount.length; i++) {
   185              require(unlockTime < _unlockTime[i], "Unlock time is not in ascending order.");
   186              require(_unlockAmount[i] > 0, "Amount is not positive number.");
   187              unlockTime = _unlockTime[i];
   188          }
   189  
   190          contractValidator = _contractValidator;
   191          nodeId = _nodeId;
   192          rewardAddress = _rewardAddress;
   193          adminList = _cnAdminlist;
   194          requirement = _requirement;
   195          lockupConditions.unlockTime = _unlockTime;
   196          lockupConditions.unlockAmount = _unlockAmount;
   197  
   198          isInitialized = false;
   199          emit DeployContract(CONTRACT_TYPE, _contractValidator, _nodeId, _rewardAddress, _cnAdminlist, _requirement, _unlockTime, _unlockAmount);
   200      }
   201  
   202  
   203  
   204      function reviewInitialConditions() external
   205      onlyAdmin(msg.sender)
   206      beforeInit() {
   207          require(lockupConditions.reviewedAdmin[msg.sender] == false, "Msg.sender already reviewed.");
   208  
   209          lockupConditions.reviewedAdmin[msg.sender] = true;
   210          lockupConditions.reviewedCount = lockupConditions.reviewedCount.add(1);
   211          
   212          emit ReviewInitialConditions(msg.sender);
   213  
   214  
   215          if(lockupConditions.reviewedCount == adminList.length + 1) {
   216              lockupConditions.allReviewed = true;
   217              emit CompleteReviewInitialConditions();
   218          }
   219      }
   220  
   221  
   222      function depositLockupStakingAndInit() external payable
   223      beforeInit() {
   224          uint256 requiredStakingAmount;
   225          uint256 cnt = lockupConditions.unlockAmount.length;
   226          for(uint256 i = 0; i < cnt; i++) {
   227              requiredStakingAmount = requiredStakingAmount.add(lockupConditions.unlockAmount[i]);
   228          }
   229          require(lockupConditions.allReviewed == true, "Reviewing is not finished.");
   230          require(msg.value == requiredStakingAmount, "Value does not match.");
   231  
   232          isAdmin[contractValidator] = false;
   233          delete contractValidator;
   234  
   235          initialLockupStaking = requiredStakingAmount;
   236          remainingLockupStaking = requiredStakingAmount;
   237          isInitialized = true;
   238          emit DepositLockupStakingAndInit(msg.sender, msg.value);
   239      }
   240  
   241  
   242      /*
   243       *  Submit multisig function request
   244       */
   245  
   246      /// @notice submit a request to add new admin at consensus node (multi-sig operation)
   247      /// @param _admin new admin address to be added
   248      function submitAddAdmin(address _admin) external
   249      afterInit()
   250      adminDoesNotExist(_admin)
   251      notNull(_admin)
   252      onlyAdmin(msg.sender)
   253      validRequirement(adminList.length.add(1), requirement) {
   254          uint256 id = requestCount;
   255          submitRequest(id, Functions.AddAdmin, bytes32(_admin), 0, 0);
   256          confirmRequest(id, requestMap[id].functionId, requestMap[id].firstArg, requestMap[id].secondArg, requestMap[id].thirdArg);
   257      }
   258  
   259      /// @notice submit a request to delete an admin at consensus node (multi-sig operation)
   260      /// @param _admin address of the admin to be deleted
   261      function submitDeleteAdmin(address _admin) external
   262      afterInit()
   263      onlyAdmin(_admin)
   264      notNull(_admin)
   265      onlyAdmin(msg.sender)
   266      validRequirement(adminList.length.sub(1), requirement) {
   267          uint256 id = requestCount;
   268          submitRequest(id, Functions.DeleteAdmin, bytes32(_admin), 0, 0);
   269          confirmRequest(id, requestMap[id].functionId, requestMap[id].firstArg, requestMap[id].secondArg, requestMap[id].thirdArg);
   270      }
   271  
   272      /// @notice submit a request to update the confirmation threshold (multi-sig operation)
   273      /// @param _requirement new confirmation threshold
   274      function submitUpdateRequirement(uint256 _requirement) external 
   275      afterInit()
   276      onlyAdmin(msg.sender)
   277      validRequirement(adminList.length, _requirement) {
   278          require(_requirement != requirement, "Invalid value");
   279          uint256 id = requestCount;
   280          submitRequest(id, Functions.UpdateRequirement, bytes32(_requirement), 0, 0);
   281          confirmRequest(id, requestMap[id].functionId, requestMap[id].firstArg, requestMap[id].secondArg, requestMap[id].thirdArg);
   282      }
   283  
   284      /// @notice submit a request to clear all unfinalized request (multi-sig operation)
   285      function submitClearRequest() external 
   286      afterInit()
   287      onlyAdmin(msg.sender) {
   288          uint256 id = requestCount;
   289          submitRequest(id, Functions.ClearRequest, 0, 0, 0);
   290          confirmRequest(id, requestMap[id].functionId, requestMap[id].firstArg, requestMap[id].secondArg, requestMap[id].thirdArg);
   291      }
   292  
   293      function submitWithdrawLockupStaking(address _to, uint256 _value) external
   294      afterInit()
   295      notNull(_to)
   296      onlyAdmin(msg.sender) {
   297          uint256 withdrawableStakingAmount;
   298          ( , , , ,withdrawableStakingAmount) = getLockupStakingInfo();
   299          require(_value > 0 && _value <= withdrawableStakingAmount, "Invalid value.");
   300  
   301          uint256 id = requestCount;
   302          submitRequest(id, Functions.WithdrawLockupStaking, bytes32(_to), bytes32(_value), 0);
   303          confirmRequest(id, requestMap[id].functionId, requestMap[id].firstArg, requestMap[id].secondArg, requestMap[id].thirdArg);
   304      }
   305  
   306      /// @notice submit a request to withdraw staked KLAY (multi-sig operation).
   307      ///         7 days after "approveStakingWithdrawal" has been requested,
   308      ///         admins can call this function for actual withdrawal for another 7-day period.
   309      ///         If the admin doesn't withraw KLAY for that 7-day period, it expires.
   310      /// @param _to target address to receive KLAY
   311      /// @param _value withdrawl amount of KLAY
   312      function submitApproveStakingWithdrawal(address _to, uint256 _value) external
   313      afterInit()
   314      notNull(_to)
   315      onlyAdmin(msg.sender) {
   316          require(_value > 0 && _value <= staking, "Invalid value.");
   317          uint256 id = requestCount;
   318          submitRequest(id, Functions.ApproveStakingWithdrawal, bytes32(_to), bytes32(_value), 0);
   319          confirmRequest(id, requestMap[id].functionId, requestMap[id].firstArg, requestMap[id].secondArg, requestMap[id].thirdArg);
   320      }
   321  
   322      /// @notice submit a request to cancel KLAY withdrawl request (multi-sig operation).
   323      /// @param _approvedWithdrawalId the withdrawal ID to cancel. The ID is acquired at the event log of ApproveStakingWithdrawal
   324      function submitCancelApprovedStakingWithdrawal(uint256 _approvedWithdrawalId) external
   325      afterInit()
   326      onlyAdmin(msg.sender) {
   327          require(withdrawalRequestMap[_approvedWithdrawalId].to != 0, "Withdrawal request does not exist.");
   328          require(withdrawalRequestMap[_approvedWithdrawalId].state == WithdrawalStakingState.Unknown, "Invalid state.");
   329  
   330          uint256 id = requestCount;
   331          submitRequest(id, Functions.CancelApprovedStakingWithdrawal, bytes32(_approvedWithdrawalId), 0, 0);
   332          confirmRequest(id, requestMap[id].functionId, requestMap[id].firstArg, requestMap[id].secondArg, requestMap[id].thirdArg);
   333      }
   334  
   335      /// @notice submit a request to update the reward address of consensus node (multi-sig operation).
   336      /// @param _rewardAddress new reward address
   337      function submitUpdateRewardAddress(address _rewardAddress) external 
   338      afterInit()
   339      notNull(_rewardAddress) 
   340      onlyAdmin(msg.sender) {
   341          uint256 id = requestCount;
   342          submitRequest(id, Functions.UpdateRewardAddress, bytes32(_rewardAddress), 0, 0);
   343          confirmRequest(id, requestMap[id].functionId, requestMap[id].firstArg, requestMap[id].secondArg, requestMap[id].thirdArg);
   344      }
   345  
   346  
   347      /*
   348       *  Confirm and revoke confirmation
   349       */
   350      /// @notice after requesting of proposer, other admin confirm the request by confirmRequest
   351      /// @param _id the request ID. It can be obtained by submitRequest event
   352      /// @param _functionId the function ID of the request. It can be obtained by submitRequest event or getRequestInfo getter function
   353      /// @param _firstArg the first argument of the request. It can be obtained by submitRequest event or getRequestInfo getter function
   354      /// @param _secondArg the second argument of the request. If there is no second argument, then it should be 0. It can be obtained by submitRequest event or getRequestInfo getter function
   355      /// @param _thirdArg the third argument of the request. If there is no second argument, then it should be 0. It can be obtained by submitRequest event or getRequestInfo getter function
   356      function confirmRequest(uint256 _id, Functions _functionId, bytes32 _firstArg, bytes32 _secondArg, bytes32 _thirdArg) public
   357      notConfirmedRequest(_id) 
   358      onlyAdmin(msg.sender) {
   359          bool hasConfirmed = false;
   360          uint256 confirmersCnt = requestMap[_id].confirmers.length;
   361          for (uint256 i = 0; i < confirmersCnt; i++) {
   362              if(msg.sender == requestMap[_id].confirmers[i]) {
   363                  hasConfirmed = true;
   364                  break;
   365              }
   366          }
   367          require(!hasConfirmed, "Msg.sender already confirmed.");
   368          require(
   369              requestMap[_id].functionId == _functionId &&
   370              requestMap[_id].firstArg == _firstArg &&
   371              requestMap[_id].secondArg == _secondArg &&
   372              requestMap[_id].thirdArg == _thirdArg, "Function id and arguments do not match.");
   373  
   374          requestMap[_id].confirmers.push(msg.sender);
   375  
   376          address[] memory confirmers = requestMap[_id].confirmers;
   377          emit ConfirmRequest(_id, msg.sender, _functionId, _firstArg, _secondArg, _thirdArg, confirmers);
   378  
   379  
   380          if (checkQuorum(_id)) {
   381              executeRequest(_id);
   382          }
   383      }
   384  
   385      /// @notice revoking confirmation of each admin. If the admin is proposer, then the request will be cancled regardless of confirmation of other admins.
   386      /// @param _id the request ID. It can be obtained by submitRequest event
   387      /// @param _functionId the function ID of the request. It can be obtained by submitRequest event or getRequestInfo getter function
   388      /// @param _firstArg the first argument of the request. It can be obtained by submitRequest event or getRequestInfo getter function
   389      /// @param _secondArg the second argument of the request. If there is no second argument, then it should be 0. It can be obtained by submitRequest event or getRequestInfo getter function
   390      /// @param _thirdArg the third argument of the request. If there is no second argument, then it should be 0. It can be obtained by submitRequest event or getRequestInfo getter function
   391      function revokeConfirmation(uint256 _id, Functions _functionId, bytes32 _firstArg, bytes32 _secondArg, bytes32 _thirdArg) external
   392      notConfirmedRequest(_id)
   393      onlyAdmin(msg.sender) {
   394          bool hasConfirmed = false;
   395          uint256 confirmersCnt = requestMap[_id].confirmers.length;
   396          for (uint256 i = 0; i < confirmersCnt; i++) {
   397              if(msg.sender == requestMap[_id].confirmers[i]) {
   398                  hasConfirmed = true;
   399                  break;
   400              }
   401          }
   402          require(hasConfirmed, "Msg.sender has not confirmed.");
   403          require(
   404              requestMap[_id].functionId == _functionId &&
   405              requestMap[_id].firstArg == _firstArg &&
   406              requestMap[_id].secondArg == _secondArg &&
   407              requestMap[_id].thirdArg == _thirdArg, "Function id and arguments do not match.");
   408  
   409          revokeHandler(_id);
   410      }
   411  
   412      // this function separated from revokeConfirmation function because of 'stack too deep' issue of solidity
   413      function revokeHandler(uint256 _id) private {
   414  
   415          if (requestMap[_id].requestProposer == msg.sender) {
   416              requestMap[_id].state = RequestState.Canceled;
   417              emit CancelRequest(_id, msg.sender, requestMap[_id].functionId, requestMap[_id].firstArg, requestMap[_id].secondArg, requestMap[_id].thirdArg);
   418          } else {
   419              deleteFromConfirmerList(_id, msg.sender);
   420              emit RevokeConfirmation(_id, msg.sender, requestMap[_id].functionId, requestMap[_id].firstArg, requestMap[_id].secondArg, requestMap[_id].thirdArg, requestMap[_id].confirmers);
   421          }
   422      }
   423  
   424  
   425      /*
   426       *  Multisig functions
   427       */
   428      function addAdmin(address _admin) external 
   429      onlyMultisigTx()
   430      adminDoesNotExist(_admin)
   431      validRequirement(adminList.length.add(1), requirement) {
   432          isAdmin[_admin] = true;
   433          adminList.push(_admin);
   434          clearRequest();
   435          emit AddAdmin(_admin);
   436      }
   437  
   438      function deleteAdmin(address _admin) external
   439      onlyMultisigTx()
   440      onlyAdmin(_admin)
   441      validRequirement(adminList.length.sub(1), requirement) {
   442          isAdmin[_admin] = false;
   443          
   444          uint256 adminCnt = adminList.length;
   445          for (uint256 i = 0; i < adminCnt - 1; i++) {
   446              if (adminList[i] == _admin) {
   447                  adminList[i] = adminList[adminCnt - 1];
   448                  break;
   449              }
   450          }
   451          delete adminList[adminCnt - 1];
   452          adminList.length = adminList.length.sub(1);
   453          clearRequest();
   454          emit DeleteAdmin(_admin);
   455      }
   456  
   457      function updateRequirement(uint256 _requirement) external 
   458      onlyMultisigTx()
   459      validRequirement(adminList.length, _requirement) {
   460          requirement = _requirement;
   461          clearRequest();
   462          emit UpdateRequirement(_requirement);
   463      }
   464  
   465      function clearRequest() public
   466      onlyMultisigTx() {
   467          for (uint256 i = lastClearedId; i < requestCount; i++){
   468              if (requestMap[i].state == RequestState.NotConfirmed) {
   469                  requestMap[i].state = RequestState.Canceled;
   470              }
   471          }
   472          lastClearedId = requestCount;
   473          emit ClearRequest();
   474      }
   475  
   476      function withdrawLockupStaking(address _to, uint256 _value) external 
   477      onlyMultisigTx() {
   478          uint256 withdrawableStakingAmount;
   479          ( , , , ,withdrawableStakingAmount) = getLockupStakingInfo();
   480          require(withdrawableStakingAmount >= _value, "Value is not withdrawable.");
   481          remainingLockupStaking = remainingLockupStaking.sub(_value);
   482  
   483          _to.transfer(_value);
   484          emit WithdrawLockupStaking(_to, _value);
   485      }
   486  
   487  
   488      function approveStakingWithdrawal(address _to, uint256 _value) external 
   489      onlyMultisigTx() {
   490          require(_value <= staking, "Value is not withdrawable.");
   491          uint256 approvedWithdrawalId = withdrawalRequestCount;
   492          withdrawalRequestMap[approvedWithdrawalId] = WithdrawalRequest({
   493              to : _to,
   494              value : _value,
   495              withdrawableFrom : now + ONE_WEEK,
   496              state: WithdrawalStakingState.Unknown
   497          });
   498          withdrawalRequestCount = withdrawalRequestCount.add(1);
   499          emit ApproveStakingWithdrawal(approvedWithdrawalId, _to, _value, now + ONE_WEEK);
   500      }
   501  
   502  
   503      function cancelApprovedStakingWithdrawal(uint256 _approvedWithdrawalId) external 
   504      onlyMultisigTx() {
   505          require(withdrawalRequestMap[_approvedWithdrawalId].to != 0, "Withdrawal request does not exist.");
   506          require(withdrawalRequestMap[_approvedWithdrawalId].state == WithdrawalStakingState.Unknown, "Invalid state.");
   507  
   508          withdrawalRequestMap[_approvedWithdrawalId].state = WithdrawalStakingState.Canceled;
   509          emit CancelApprovedStakingWithdrawal(_approvedWithdrawalId, withdrawalRequestMap[_approvedWithdrawalId].to, withdrawalRequestMap[_approvedWithdrawalId].value);
   510      }
   511  
   512  
   513      function updateRewardAddress(address _rewardAddress) external 
   514      onlyMultisigTx() {
   515          rewardAddress = _rewardAddress;
   516          AddressBookInterface(ADDRESS_BOOK_ADDRESS).reviseRewardAddress(_rewardAddress);
   517          emit UpdateRewardAddress(rewardAddress);
   518      }
   519  
   520  
   521      /*
   522       * Public functions
   523       */
   524      /// @notice stake KLAY
   525      function stakeKlay() external payable 
   526      afterInit() {
   527          require(msg.value > 0, "Invalid amount.");
   528          staking = staking.add(msg.value);
   529          emit StakeKlay(msg.sender, msg.value);
   530      }
   531  
   532      /// @notice stake KLAY fallback function
   533      function () external payable 
   534      afterInit() {
   535          require(msg.value > 0, "Invalid amount.");
   536          staking = staking.add(msg.value);
   537          emit StakeKlay(msg.sender, msg.value);
   538      }
   539  
   540      /// @notice 7 days after "approveStakingWithdrawal" has been requested, admins can call this function for actual withdrawal. However, it's only available for 7 days
   541      /// @param _approvedWithdrawalId the withdrawal ID to excute. The ID is acquired at the event log of ApproveStakingWithdrawal
   542      function withdrawApprovedStaking(uint256 _approvedWithdrawalId) external 
   543      onlyAdmin(msg.sender) {
   544          require(withdrawalRequestMap[_approvedWithdrawalId].to != 0, "Withdrawal request does not exist.");
   545          require(withdrawalRequestMap[_approvedWithdrawalId].state == WithdrawalStakingState.Unknown, "Invalid state.");
   546          require(withdrawalRequestMap[_approvedWithdrawalId].value <= staking, "Value is not withdrawable.");
   547          require(now >= withdrawalRequestMap[_approvedWithdrawalId].withdrawableFrom, "Not withdrawable yet.");
   548          if (now >= withdrawalRequestMap[_approvedWithdrawalId].withdrawableFrom + ONE_WEEK) {
   549  
   550              withdrawalRequestMap[_approvedWithdrawalId].state = WithdrawalStakingState.Canceled;
   551              emit CancelApprovedStakingWithdrawal(_approvedWithdrawalId, withdrawalRequestMap[_approvedWithdrawalId].to, withdrawalRequestMap[_approvedWithdrawalId].value);
   552          } else {
   553              staking = staking.sub(withdrawalRequestMap[_approvedWithdrawalId].value);
   554              withdrawalRequestMap[_approvedWithdrawalId].state = WithdrawalStakingState.Transferred;
   555              withdrawalRequestMap[_approvedWithdrawalId].to.transfer(withdrawalRequestMap[_approvedWithdrawalId].value);
   556              emit WithdrawApprovedStaking(_approvedWithdrawalId, withdrawalRequestMap[_approvedWithdrawalId].to, withdrawalRequestMap[_approvedWithdrawalId].value);
   557          }
   558      }
   559  
   560  
   561      /*
   562       * Private functions
   563       */
   564      /// @dev 
   565      function deleteFromConfirmerList(uint256 _id, address _admin) private {
   566          uint256 confirmersCnt = requestMap[_id].confirmers.length;
   567          for(uint256 i = 0; i < confirmersCnt; i++){
   568              if(_admin == requestMap[_id].confirmers[i]){
   569  
   570  
   571                  if(i != confirmersCnt - 1) {
   572                      requestMap[_id].confirmers[i] = requestMap[_id].confirmers[confirmersCnt - 1];
   573                  }
   574  
   575                  delete requestMap[_id].confirmers[confirmersCnt - 1];
   576                  requestMap[_id].confirmers.length = confirmersCnt.sub(1);
   577                  break;
   578              }
   579          }
   580      }
   581  
   582      function submitRequest(uint256 _id, Functions _functionId, bytes32 _firstArg, bytes32 _secondArg, bytes32 _thirdArg) private {
   583          requestMap[_id] = Request({
   584              functionId : _functionId,
   585              firstArg : _firstArg,
   586              secondArg : _secondArg,
   587              thirdArg : _thirdArg,
   588              requestProposer : msg.sender,
   589              confirmers : new address[](0),
   590              state: RequestState.NotConfirmed
   591          });
   592          emit SubmitRequest(_id, msg.sender, _functionId, _firstArg, _secondArg, _thirdArg);
   593  
   594          requestCount = requestCount.add(1);
   595      }
   596  
   597      function executeRequest(uint256 _id) private {
   598          bool executed = false;
   599          if (requestMap[_id].functionId == Functions.AddAdmin) {
   600              //bytes4(keccak256("addAdmin(address)")) => 0x70480275
   601              executed = address(this).call(0x70480275, address(requestMap[_id].firstArg));
   602          }
   603  
   604          if (requestMap[_id].functionId == Functions.DeleteAdmin) {
   605              //bytes4(keccak256("deleteAdmin(address)")) => 0x27e1f7df
   606              executed = address(this).call(0x27e1f7df, address(requestMap[_id].firstArg));
   607          }
   608  
   609          if (requestMap[_id].functionId == Functions.UpdateRequirement) {
   610              //bytes4(keccak256("updateRequirement(uint256)")) => 0xc47afb3a
   611              executed = address(this).call(0xc47afb3a, uint256(requestMap[_id].firstArg));
   612          }
   613  
   614          if (requestMap[_id].functionId == Functions.ClearRequest) {
   615              //bytes4(keccak256("clearRequest()")) => 0x4f97638f
   616              executed = address(this).call(0x4f97638f);
   617          }
   618  
   619          if (requestMap[_id].functionId == Functions.WithdrawLockupStaking) {
   620              //bytes4(keccak256("withdrawLockupStaking(address,uint256)")) => 0x505ebed4
   621              executed = address(this).call(0x505ebed4, address(requestMap[_id].firstArg), uint256(requestMap[_id].secondArg)); 
   622          }
   623  
   624          if (requestMap[_id].functionId == Functions.ApproveStakingWithdrawal) {
   625              //bytes4(keccak256("approveStakingWithdrawal(address,uint256)")) => 0x5df8b09a
   626              executed = address(this).call(0x5df8b09a, address(requestMap[_id].firstArg), uint256(requestMap[_id].secondArg));
   627          }
   628  
   629          if (requestMap[_id].functionId == Functions.CancelApprovedStakingWithdrawal) {
   630              //bytes4(keccak256("cancelApprovedStakingWithdrawal(uint256)")) => 0xc804b115
   631              executed = address(this).call(0xc804b115, uint256(requestMap[_id].firstArg));
   632          }
   633  
   634          if (requestMap[_id].functionId == Functions.UpdateRewardAddress) {
   635              //bytes4(keccak256("updateRewardAddress(address)")) => 0x944dd5a2
   636              executed = address(this).call(0x944dd5a2, address(requestMap[_id].firstArg));
   637          }
   638  
   639          if(executed) {
   640              requestMap[_id].state = RequestState.Executed;
   641              emit ExecuteRequestSuccess(_id, msg.sender, requestMap[_id].functionId, requestMap[_id].firstArg, requestMap[_id].secondArg, requestMap[_id].thirdArg);
   642          } else {
   643              requestMap[_id].state = RequestState.ExecutionFailed;
   644              emit ExecuteRequestFailure(_id, msg.sender, requestMap[_id].functionId, requestMap[_id].firstArg, requestMap[_id].secondArg, requestMap[_id].thirdArg);
   645          }
   646      }
   647  
   648      function checkQuorum(uint256 _id) private view returns(bool) {
   649  
   650          return (requestMap[_id].confirmers.length >= requirement); 
   651      }
   652  
   653  
   654      /*
   655       * Getter functions
   656       */
   657      /// @dev 
   658      function getReviewers() external view 
   659      beforeInit() 
   660      returns(address[]) {
   661          address[] memory reviewers = new address[](lockupConditions.reviewedCount);
   662          uint256 id = 0;
   663          if(lockupConditions.reviewedAdmin[contractValidator] == true) {
   664              reviewers[id] = contractValidator;
   665              id ++;
   666          }
   667          for(uint256 i = 0; i < adminList.length; i ++) {
   668              if(lockupConditions.reviewedAdmin[adminList[i]] == true) {
   669                  reviewers[id] = adminList[i];
   670                  id ++;
   671              }
   672          }
   673          return reviewers;
   674      }
   675  
   676      /// @notice Queries for request id that matches entered state
   677      /// @param _from beginning index
   678      /// @param _to last index (if 0 or greater than total request count, it loops the whole list)
   679      /// @param _state request state
   680      /// @return uint256[] request IDs satisfying the conditions
   681      function getRequestIds(uint256 _from, uint256 _to, RequestState _state) external view returns(uint256[]) {
   682          uint256 lastIndex = _to;
   683          if (_to == 0 || _to >= requestCount) {
   684              lastIndex = requestCount;
   685          }
   686          require(lastIndex >= _from);
   687  
   688          uint256 cnt = 0;
   689          uint256 i;
   690  
   691          for (i = _from; i < lastIndex; i++) {
   692              if (requestMap[i].state == _state) {
   693                  cnt += 1;
   694              }  
   695          }
   696          uint256[] memory requestIds = new uint256[](cnt);
   697          cnt = 0;
   698          for (i = _from; i < lastIndex; i++) {
   699              if (requestMap[i].state == _state) {
   700                  requestIds[cnt] = i;
   701                  cnt += 1;
   702              }
   703          }
   704          return requestIds;
   705      }
   706  
   707      /// @notice get details of a request
   708      /// @param _id request ID
   709      /// @return function ID, first argument, second argument, third argument, request proposer, confirmers of the request, state of the request
   710      function getRequestInfo(uint256 _id) external view returns(
   711          Functions,
   712          bytes32,
   713          bytes32,
   714          bytes32,
   715          address,
   716          address[],
   717          RequestState) {
   718          return(
   719              requestMap[_id].functionId,
   720              requestMap[_id].firstArg,
   721              requestMap[_id].secondArg,
   722              requestMap[_id].thirdArg,
   723              requestMap[_id].requestProposer,
   724              requestMap[_id].confirmers,
   725              requestMap[_id].state
   726          );
   727      }
   728  
   729      function getLockupStakingInfo() public view
   730      afterInit()
   731      returns(uint256[], uint256[], uint256, uint256, uint256) {
   732          uint256 currentTime = now;
   733          uint256 unlockedAmount = 0;
   734  
   735          uint256 cnt = lockupConditions.unlockTime.length;
   736          for (uint256 i = 0; i < cnt; i++){
   737              if(currentTime > lockupConditions.unlockTime[i]) {
   738                  unlockedAmount = unlockedAmount.add(lockupConditions.unlockAmount[i]);
   739              }
   740          }
   741          uint256 amountWithdrawn = initialLockupStaking.sub(remainingLockupStaking);
   742          uint256 withdrawableLockupStaking = unlockedAmount.sub(amountWithdrawn);
   743  
   744          return (lockupConditions.unlockTime, lockupConditions.unlockAmount, initialLockupStaking, remainingLockupStaking, withdrawableLockupStaking);
   745      }
   746  
   747      /// @notice loops withdrawalRequestMap and returns aprroved withdrawal ids
   748      /// @param _from beginning index
   749      /// @param _to last index (if 0 or greater than total request count, it loops the whole list)
   750      /// @param _state withdrawal staking state
   751      /// @return withdrawal IDs satisfying the conditions
   752      function getApprovedStakingWithdrawalIds(uint256 _from, uint256 _to, WithdrawalStakingState _state) external view returns(uint256[]) {
   753          uint256 lastIndex = _to;
   754          if (_to == 0 || _to >= withdrawalRequestCount) {
   755              lastIndex = withdrawalRequestCount;
   756          }
   757          require(lastIndex >= _from, "Invalid index.");
   758  
   759          uint256 cnt = 0;
   760          uint256 i;
   761  
   762          for (i = _from; i < lastIndex; i++) {
   763              if (withdrawalRequestMap[i].state == _state) {
   764                  cnt += 1;
   765              }  
   766          }
   767          uint256[] memory approvedWithdrawalIds = new uint256[](cnt);
   768          cnt = 0;
   769          for (i = _from; i < lastIndex; i++) {
   770              if (withdrawalRequestMap[i].state == _state) {
   771                  approvedWithdrawalIds[cnt] = i;
   772                  cnt += 1;
   773              }
   774          }
   775          return approvedWithdrawalIds;
   776      }
   777  
   778      /// @notice get details of approved staking withdrawal
   779      /// @param _index staking withdrawal ID
   780      /// @return withdrawal target address, wthdrawal KLAY value, time when it becomes available, withdrawal request state
   781      function getApprovedStakingWithdrawalInfo(uint256 _index) external view returns(address, uint256, uint256, WithdrawalStakingState) {
   782          return (
   783              withdrawalRequestMap[_index].to,
   784              withdrawalRequestMap[_index].value,
   785              withdrawalRequestMap[_index].withdrawableFrom,
   786              withdrawalRequestMap[_index].state
   787          );
   788      }
   789  
   790      function getState() external view 
   791      returns(address,
   792              address,
   793              address,
   794              address[],
   795              uint256,
   796              uint256[],
   797              uint256[],
   798              bool,
   799              bool) {
   800          return (
   801              contractValidator,
   802              nodeId,
   803              rewardAddress,
   804              adminList,
   805              requirement,
   806              lockupConditions.unlockTime,
   807              lockupConditions.unlockAmount,
   808              lockupConditions.allReviewed,
   809              isInitialized
   810          );
   811      }
   812  }
   813  
   814  interface AddressBookInterface {
   815      function reviseRewardAddress(address) external;
   816  }