pragma solidity 0.5.11; // solium-disable-next-line pragma experimental ABIEncoderV2; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { require(newOwner != address(0)); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } } contract ACL is Ownable { event AccessRights(address indexed from, address indexed actor, address indexed target, bytes4[] methods, bool[] access); /// @dev Actor => Contract => Function signature mapping(address => mapping(address => mapping(bytes4 => bool))) _accessRights; /** * @dev check whether msg.sender has access rights to execute '_data' at '_target' * @param _actor actor to check rights for * @param _target target contract address * @param _method 4-byte identifier of the method * @return true/false whether 'msg.sender' has sufficient rights */ function accessRights(address _actor, address _target, bytes4 _method) public view returns (bool) { return _actor == owner || _accessRights[_actor][_target][_method]; } /** * @dev set access rights for for an Ethereum address * @param _actor ethereum address to grant access rights * @param _target target contract address '_actor' is allowed to call * @param _methods list of methods on '_target' contract the '_actor' is allowed to call * @param _access list of booleans indicating access rights corresponding to the list of '_methods' */ function setAccessRights(address _actor, address _target, bytes4[] memory _methods, bool[] memory _access) public { // Check that 'msg.sender' has sufficient rights to alter ACL rules require(accessRights(msg.sender, address(this), getMethodSignature(msg.data)), "access denied"); for (uint256 i = 0; i < _methods.length; i++) { _accessRights[_actor][_target][_methods[i]] = _access[i]; } emit AccessRights(msg.sender, _actor, _target, _methods, _access); } function getMethodSignature(bytes memory _data) internal pure returns (bytes4 method) { assembly { method := mload(add(_data, 0x20)) } } } contract Staging is ACL { struct Update { address target; bytes data; } struct StagedUpdate { Update[] updates; uint256 timelock; } /// @dev UpdateStaged is emitted when an actor stages an update event UpdateStaged(address indexed actor, uint256 id, StagedUpdate stagedUpdate); mapping(address => uint256) delays; mapping(uint256 => StagedUpdate) public stagedUpdates; uint256 public updateCount; /** * @dev stage an update for future execution. * @param _updates a list of updates to be executed. * @notice The entity staging the update must be allowed. */ function stageUpdate(Update[] memory _updates) public { StagedUpdate storage stagedUpdate = stagedUpdates[updateCount]; for (uint256 i= 0; i < _updates.length; i++) { require(accessRights(msg.sender, _updates[i].target, getMethodSignature(_updates[i].data)), "access denied"); stagedUpdate.timelock = block.number + delays[msg.sender]; stagedUpdate.updates.push(Update({target: _updates[i].target, data: _updates[i].data})); } emit UpdateStaged(msg.sender, updateCount, stagedUpdate); updateCount++; } function setDelay(address _actor, uint256 _delay) public { require(accessRights(msg.sender, address(this), getMethodSignature(msg.data)), "access denied"); if (_delay == 0) { delete delays[_actor]; } else { delays[_actor] = _delay; } } } contract Execution is ACL, Staging { /// @dev UpdateExecuted is emitted when a staged update has been fully executed event UpdateExecuted(address indexed actor, StagedUpdate update); /** * @dev Execute a staged update. * @notice Updates are authorized during staging. * @notice Reverts if a transaction can not be executed. * @param _id id of the staged update. */ function executeUpdate(uint256 _id) public { StagedUpdate storage stagedUpdate = stagedUpdates[_id]; require(block.number > stagedUpdate.timelock, "time delay for update not expired"); for (uint256 i = 0; i < stagedUpdate.updates.length; i++) { (bool success,) = stagedUpdate.updates[i].target.call(stagedUpdate.updates[i].data); require(success, "could not execute update"); } emit UpdateExecuted(msg.sender, stagedUpdate); delete stagedUpdates[_id]; } } contract Governance is ACL, Staging, Execution { function setAccessRightsAndDelay(address _actor, address _target, bytes4[] memory _methods, bool[] memory _access, uint256 _delay) public { super.setAccessRights(_actor, _target, _methods, _access); super.setDelay(_actor, _delay); } }
0.5.11