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 }