github.com/klaytn/klaytn@v1.12.1/contracts/gov/GovParam.sol (about)

     1  // SPDX-License-Identifier: LGPL-3.0-only
     2  // Sources flattened with hardhat v2.12.6 https://hardhat.org
     3  
     4  // File @openzeppelin/contracts/utils/Context.sol@v4.6.0
     5  
     6  
     7  // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
     8  
     9  pragma solidity ^0.8.0;
    10  
    11  /**
    12   * @dev Provides information about the current execution context, including the
    13   * sender of the transaction and its data. While these are generally available
    14   * via msg.sender and msg.data, they should not be accessed in such a direct
    15   * manner, since when dealing with meta-transactions the account sending and
    16   * paying for execution may not be the actual sender (as far as an application
    17   * is concerned).
    18   *
    19   * This contract is only required for intermediate, library-like contracts.
    20   */
    21  abstract contract Context {
    22      function _msgSender() internal view virtual returns (address) {
    23          return msg.sender;
    24      }
    25  
    26      function _msgData() internal view virtual returns (bytes calldata) {
    27          return msg.data;
    28      }
    29  }
    30  
    31  
    32  // File @openzeppelin/contracts/access/Ownable.sol@v4.6.0
    33  
    34  
    35  // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
    36  
    37  pragma solidity ^0.8.0;
    38  
    39  /**
    40   * @dev Contract module which provides a basic access control mechanism, where
    41   * there is an account (an owner) that can be granted exclusive access to
    42   * specific functions.
    43   *
    44   * By default, the owner account will be the one that deploys the contract. This
    45   * can later be changed with {transferOwnership}.
    46   *
    47   * This module is used through inheritance. It will make available the modifier
    48   * `onlyOwner`, which can be applied to your functions to restrict their use to
    49   * the owner.
    50   */
    51  abstract contract Ownable is Context {
    52      address private _owner;
    53  
    54      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    55  
    56      /**
    57       * @dev Initializes the contract setting the deployer as the initial owner.
    58       */
    59      constructor() {
    60          _transferOwnership(_msgSender());
    61      }
    62  
    63      /**
    64       * @dev Returns the address of the current owner.
    65       */
    66      function owner() public view virtual returns (address) {
    67          return _owner;
    68      }
    69  
    70      /**
    71       * @dev Throws if called by any account other than the owner.
    72       */
    73      modifier onlyOwner() {
    74          require(owner() == _msgSender(), "Ownable: caller is not the owner");
    75          _;
    76      }
    77  
    78      /**
    79       * @dev Leaves the contract without owner. It will not be possible to call
    80       * `onlyOwner` functions anymore. Can only be called by the current owner.
    81       *
    82       * NOTE: Renouncing ownership will leave the contract without an owner,
    83       * thereby removing any functionality that is only available to the owner.
    84       */
    85      function renounceOwnership() public virtual onlyOwner {
    86          _transferOwnership(address(0));
    87      }
    88  
    89      /**
    90       * @dev Transfers ownership of the contract to a new account (`newOwner`).
    91       * Can only be called by the current owner.
    92       */
    93      function transferOwnership(address newOwner) public virtual onlyOwner {
    94          require(newOwner != address(0), "Ownable: new owner is the zero address");
    95          _transferOwnership(newOwner);
    96      }
    97  
    98      /**
    99       * @dev Transfers ownership of the contract to a new account (`newOwner`).
   100       * Internal function without access restriction.
   101       */
   102      function _transferOwnership(address newOwner) internal virtual {
   103          address oldOwner = _owner;
   104          _owner = newOwner;
   105          emit OwnershipTransferred(oldOwner, newOwner);
   106      }
   107  }
   108  
   109  
   110  // File contracts/IGovParam.sol
   111  
   112  // Copyright 2022 The klaytn Authors
   113  // This file is part of the klaytn library.
   114  //
   115  // The klaytn library is free software: you can redistribute it and/or modify
   116  // it under the terms of the GNU Lesser General Public License as published by
   117  // the Free Software Foundation, either version 3 of the License, or
   118  // (at your option) any later version.
   119  //
   120  // The klaytn library is distributed in the hope that it will be useful,
   121  // but WITHOUT ANY WARRANTY; without even the implied warranty of
   122  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   123  // GNU Lesser General Public License for more details.
   124  //
   125  // You should have received a copy of the GNU Lesser General Public License
   126  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
   127  
   128  
   129  pragma solidity ^0.8.0;
   130  
   131  /**
   132   * @dev Interface of the GovParam Contract
   133   */
   134  interface IGovParam {
   135      struct Param {
   136          uint256 activation;
   137          bool exists;
   138          bytes val;
   139      }
   140  
   141      event SetParam(string name, bool exists, bytes value, uint256 activation);
   142  
   143      function setParam(
   144          string calldata name, bool exists, bytes calldata value,
   145          uint256 activation) external;
   146  
   147      function setParamIn(
   148          string calldata name, bool exists, bytes calldata value,
   149          uint256 relativeActivation) external;
   150  
   151      /// All (including soft-deleted) param names ever existed
   152      function paramNames(uint256 idx) external view returns (string memory);
   153      function getAllParamNames() external view returns (string[] memory);
   154  
   155      /// Raw checkpoints
   156      function checkpoints(string calldata name) external view
   157          returns(Param[] memory);
   158      function getAllCheckpoints() external view
   159          returns(string[] memory, Param[][] memory);
   160  
   161      /// Any given stored (including soft-deleted) params
   162      function getParam(string calldata name) external view
   163          returns(bool, bytes memory);
   164      function getParamAt(string calldata name, uint256 blockNumber) external view
   165          returns(bool, bytes memory);
   166  
   167      /// All existing params
   168      function getAllParams() external view
   169          returns (string[] memory, bytes[] memory);
   170      function getAllParamsAt(uint256 blockNumber) external view
   171          returns(string[] memory, bytes[] memory);
   172  }
   173  
   174  
   175  // File contracts/GovParam.sol
   176  
   177  // Copyright 2022 The klaytn Authors
   178  // This file is part of the klaytn library.
   179  //
   180  // The klaytn library is free software: you can redistribute it and/or modify
   181  // it under the terms of the GNU Lesser General Public License as published by
   182  // the Free Software Foundation, either version 3 of the License, or
   183  // (at your option) any later version.
   184  //
   185  // The klaytn library is distributed in the hope that it will be useful,
   186  // but WITHOUT ANY WARRANTY; without even the implied warranty of
   187  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   188  // GNU Lesser General Public License for more details.
   189  //
   190  // You should have received a copy of the GNU Lesser General Public License
   191  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
   192  
   193  
   194  pragma solidity ^0.8.0;
   195  
   196  
   197  /// @dev Contract to store and update governance parameters
   198  /// This contract can be called by node to read the param values in the current block
   199  /// Also, the governance contract can change the parameter values.
   200  contract GovParam is Ownable, IGovParam {
   201      /// @dev Returns all parameter names that ever existed
   202      string[] public override paramNames;
   203  
   204      mapping(string => Param[]) private _checkpoints;
   205  
   206      /// @dev Returns all parameter names that ever existed, including those that are currently non-existing
   207      function getAllParamNames() external view override returns (string[] memory) {
   208          return paramNames;
   209      }
   210  
   211      /// @dev Returns all checkpoints of the parameter
   212      /// @param name The parameter name
   213      function checkpoints(string calldata name) public view override returns (Param[] memory) {
   214          return _checkpoints[name];
   215      }
   216  
   217      /// @dev Returns the last checkpoint whose activation block has passed.
   218      ///      WARNING: Before calling this function, you must ensure that
   219      ///               _checkpoints[name].length > 0
   220      function _param(string memory name) private view returns (Param storage) {
   221          Param[] storage ckpts = _checkpoints[name];
   222          uint256 len = ckpts.length;
   223  
   224          // there can be up to one checkpoint whose activation block has not passed yet
   225          // because setParam() will overwrite if there already exists such a checkpoint
   226          // thus, if the last checkpoint's activation is in the future,
   227          // it is guaranteed that the next-to-last is activated
   228          if (ckpts[len - 1].activation <= block.number) {
   229              return ckpts[len - 1];
   230          } else {
   231              return ckpts[len - 2];
   232          }
   233      }
   234  
   235      /// @dev Returns the parameter viewed by the current block
   236      /// @param name The parameter name
   237      /// @return (1) Whether the parameter exists, and if the parameter exists, (2) its value
   238      function getParam(string calldata name) external view override returns (bool, bytes memory) {
   239          if (_checkpoints[name].length == 0) {
   240              return (false, "");
   241          }
   242  
   243          Param memory p = _param(name);
   244          return (p.exists, p.val);
   245      }
   246  
   247      /// @dev Average of two integers without overflow
   248      /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.3/contracts/utils/math/Math.sol#L34
   249      function average(uint256 a, uint256 b) internal pure returns (uint256) {
   250          // (a + b) / 2 can overflow.
   251          return (a & b) + (a ^ b) / 2;
   252      }
   253  
   254      /// @dev Returns the parameters used for generating the "blockNumber" block
   255      ///      WARNING: for future blocks, the result may change
   256      function getParamAt(string memory name, uint256 blockNumber) public view override returns (bool, bytes memory) {
   257          uint256 len = _checkpoints[name].length;
   258          if (len == 0) {
   259              return (false, "");
   260          }
   261  
   262          // See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Votes.sol#L99
   263          // We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
   264          // During the loop, the index of the wanted checkpoint remains in the range [low-1, high).
   265          // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
   266          // - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
   267          // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high)
   268          // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
   269          // out of bounds (in which case we're looking too far in the past and the result is 0).
   270          // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
   271          // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
   272          // the same.
   273          uint256 low = 0;
   274          uint256 high = len;
   275  
   276          Param[] storage ckpts = _checkpoints[name];
   277  
   278          while (low < high) {
   279              uint256 mid = average(low, high);
   280              if (ckpts[mid].activation > blockNumber) {
   281                  high = mid;
   282              } else {
   283                  low = mid + 1;
   284              }
   285          }
   286  
   287          // high can't be zero. For high to be zero, The "high = mid" line should be executed when mid is zero.
   288          // When mid = 0, ckpts[mid].activation is always 0 due to the sentinel checkpoint.
   289          // Therefore, ckpts[mid].activation <= blockNumber,
   290          // and the "high = mid" line is never executed.
   291          return (ckpts[high - 1].exists, ckpts[high - 1].val);
   292      }
   293  
   294      /// @dev Returns existing parameters viewed by the current block
   295      function getAllParams() external view override returns (string[] memory, bytes[] memory) {
   296          // solidity doesn't allow memory arrays to be resized
   297          // so we calculate the size in advance (existCount)
   298          // See https://docs.soliditylang.org/en/latest/types.html#allocating-memory-arrays
   299          uint256 existCount = 0;
   300          for (uint256 i = 0; i < paramNames.length; i++) {
   301              Param storage tmp = _param(paramNames[i]);
   302              if (tmp.exists) {
   303                  existCount++;
   304              }
   305          }
   306  
   307          string[] memory names = new string[](existCount);
   308          bytes[] memory vals = new bytes[](existCount);
   309  
   310          uint256 idx = 0;
   311          for (uint256 i = 0; i < paramNames.length; i++) {
   312              Param storage tmp = _param(paramNames[i]);
   313              if (tmp.exists) {
   314                  names[idx] = paramNames[i];
   315                  vals[idx] = tmp.val;
   316                  idx++;
   317              }
   318          }
   319          return (names, vals);
   320      }
   321  
   322      /// @dev Returns parameters used for generating the "blockNumber" block
   323      ///      WARNING: for future blocks, the result may change
   324      function getAllParamsAt(uint256 blockNumber) external view override returns (string[] memory, bytes[] memory) {
   325          // solidity doesn't allow memory arrays to be resized
   326          // so we calculate the size in advance (existCount)
   327          // See https://docs.soliditylang.org/en/latest/types.html#allocating-memory-arrays
   328          uint256 existCount = 0;
   329          for (uint256 i = 0; i < paramNames.length; i++) {
   330              (bool exists, ) = getParamAt(paramNames[i], blockNumber);
   331              if (exists) {
   332                  existCount++;
   333              }
   334          }
   335  
   336          string[] memory names = new string[](existCount);
   337          bytes[] memory vals = new bytes[](existCount);
   338  
   339          uint256 idx = 0;
   340          for (uint256 i = 0; i < paramNames.length; i++) {
   341              (bool exists, bytes memory val) = getParamAt(paramNames[i], blockNumber);
   342              if (exists) {
   343                  names[idx] = paramNames[i];
   344                  vals[idx] = val;
   345                  idx++;
   346              }
   347          }
   348  
   349          return (names, vals);
   350      }
   351  
   352      /// @dev Returns all parameters as stored in the contract
   353      function getAllCheckpoints() external view override returns (string[] memory, Param[][] memory) {
   354          Param[][] memory ckptsArr = new Param[][](paramNames.length);
   355          for (uint256 i = 0; i < paramNames.length; i++) {
   356              ckptsArr[i] = _checkpoints[paramNames[i]];
   357          }
   358          return (paramNames, ckptsArr);
   359      }
   360  
   361      /// @dev Returns all parameters as stored in the contract
   362      function setParam(string calldata name, bool exists, bytes calldata val, uint256 activation)
   363          public
   364          override
   365          onlyOwner
   366      {
   367          require(bytes(name).length > 0, "GovParam: name cannot be empty");
   368          require(
   369              activation > block.number,
   370              "GovParam: activation must be in the future"
   371          );
   372          require(
   373              !exists || val.length > 0,
   374              "GovParam: val must not be empty if exists=true"
   375          );
   376          require(
   377              exists || val.length == 0,
   378              "GovParam: val must be empty if exists=false"
   379          );
   380  
   381          Param memory newParam = Param(activation, exists, val);
   382          Param[] storage ckpts = _checkpoints[name];
   383  
   384          // for a new parameter, push occurs twice
   385          // (1) sentinel checkpoint
   386          // (2) newParam
   387          // this ensures that if name is in paramNames, then ckpts.length >= 2
   388          if (ckpts.length == 0) {
   389              paramNames.push(name);
   390  
   391              // insert a sentinel checkpoint
   392              ckpts.push(Param(0, false, ""));
   393          }
   394  
   395          uint256 lastPos = ckpts.length - 1;
   396          // if the last checkpoint's activation is in the past, push newParam
   397          // otherwise, overwrite the last checkpoint with newParam
   398          if (ckpts[lastPos].activation <= block.number) {
   399              ckpts.push(newParam);
   400          } else {
   401              ckpts[lastPos] = newParam;
   402          }
   403  
   404          emit SetParam(name, exists, val, activation);
   405      }
   406  
   407      /// @dev Updates the parameter to the given state at the relative activation block
   408      function setParamIn(string calldata name, bool exists, bytes calldata val, uint256 relativeActivation)
   409          external
   410          override
   411          onlyOwner
   412      {
   413          uint256 activation = block.number + relativeActivation;
   414          setParam(name, exists, val, activation);
   415      }
   416  }