pragma solidity 0.5.17; /// @title KeepRegistry /// @notice Governance owned registry of approved contracts and roles. contract KeepRegistry { enum ContractStatus {New, Approved, Disabled} // Governance role is to enable recovery from key compromise by rekeying // other roles. Also, it can disable operator contract panic buttons // permanently. address public governance; // Registry Keeper maintains approved operator contracts. Each operator // contract must be approved before it can be authorized by a staker or // used by a service contract. address public registryKeeper; // Each operator contract has a Panic Button which can disable malicious // or malfunctioning contract that have been previously approved by the // Registry Keeper. // // New operator contract added to the registry has a default panic button // value assigned (defaultPanicButton). Panic button for each operator // contract can be later updated by Governance to individual value. // // It is possible to disable panic button for individual contract by // setting the panic button to zero address. In such case, operator contract // can not be disabled and is permanently approved in the registry. mapping(address => address) public panicButtons; // Default panic button for each new operator contract added to the // registry. Can be later updated for each contract. address public defaultPanicButton; // Each service contract has a Operator Contract Upgrader whose purpose // is to manage operator contracts for that specific service contract. // The Operator Contract Upgrader can add new operator contracts to the // service contract’s operator contract list, and deprecate old ones. mapping(address => address) public operatorContractUpgraders; // Operator contract may have a Service Contract Upgrader whose purpose is // to manage service contracts for that specific operator contract. // Service Contract Upgrader can add and remove service contracts // from the list of service contracts approved to work with the operator // contract. List of service contracts is maintained in the operator // contract and is optional - not every operator contract needs to have // a list of service contracts it wants to cooperate with. mapping(address => address) public serviceContractUpgraders; // The registry of operator contracts mapping(address => ContractStatus) public operatorContracts; event OperatorContractApproved(address operatorContract); event OperatorContractDisabled(address operatorContract); event GovernanceUpdated(address governance); event RegistryKeeperUpdated(address registryKeeper); event DefaultPanicButtonUpdated(address defaultPanicButton); event OperatorContractPanicButtonDisabled(address operatorContract); event OperatorContractPanicButtonUpdated( address operatorContract, address panicButton ); event OperatorContractUpgraderUpdated( address serviceContract, address upgrader ); event ServiceContractUpgraderUpdated( address operatorContract, address keeper ); modifier onlyGovernance() { require(governance == msg.sender, "Not authorized"); _; } modifier onlyRegistryKeeper() { require(registryKeeper == msg.sender, "Not authorized"); _; } modifier onlyPanicButton(address _operatorContract) { address panicButton = panicButtons[_operatorContract]; require(panicButton != address(0), "Panic button disabled"); require(panicButton == msg.sender, "Not authorized"); _; } modifier onlyForNewContract(address _operatorContract) { require( isNewOperatorContract(_operatorContract), "Not a new operator contract" ); _; } modifier onlyForApprovedContract(address _operatorContract) { require( isApprovedOperatorContract(_operatorContract), "Not an approved operator contract" ); _; } constructor() public { governance = msg.sender; registryKeeper = msg.sender; defaultPanicButton = msg.sender; } function setGovernance(address _governance) public onlyGovernance { governance = _governance; emit GovernanceUpdated(governance); } function setRegistryKeeper(address _registryKeeper) public onlyGovernance { registryKeeper = _registryKeeper; emit RegistryKeeperUpdated(registryKeeper); } function setDefaultPanicButton(address _panicButton) public onlyGovernance { defaultPanicButton = _panicButton; emit DefaultPanicButtonUpdated(defaultPanicButton); } function setOperatorContractPanicButton( address _operatorContract, address _panicButton ) public onlyForApprovedContract(_operatorContract) onlyGovernance { require( panicButtons[_operatorContract] != address(0), "Disabled panic button cannot be updated" ); require( _panicButton != address(0), "Panic button must be non-zero address" ); panicButtons[_operatorContract] = _panicButton; emit OperatorContractPanicButtonUpdated( _operatorContract, _panicButton ); } function disableOperatorContractPanicButton(address _operatorContract) public onlyForApprovedContract(_operatorContract) onlyGovernance { require( panicButtons[_operatorContract] != address(0), "Panic button already disabled" ); panicButtons[_operatorContract] = address(0); emit OperatorContractPanicButtonDisabled(_operatorContract); } function setOperatorContractUpgrader( address _serviceContract, address _operatorContractUpgrader ) public onlyGovernance { operatorContractUpgraders[_serviceContract] = _operatorContractUpgrader; emit OperatorContractUpgraderUpdated( _serviceContract, _operatorContractUpgrader ); } function setServiceContractUpgrader( address _operatorContract, address _serviceContractUpgrader ) public onlyGovernance { serviceContractUpgraders[_operatorContract] = _serviceContractUpgrader; emit ServiceContractUpgraderUpdated( _operatorContract, _serviceContractUpgrader ); } function approveOperatorContract(address operatorContract) public onlyForNewContract(operatorContract) onlyRegistryKeeper { operatorContracts[operatorContract] = ContractStatus.Approved; panicButtons[operatorContract] = defaultPanicButton; emit OperatorContractApproved(operatorContract); } function disableOperatorContract(address operatorContract) public onlyForApprovedContract(operatorContract) onlyPanicButton(operatorContract) { operatorContracts[operatorContract] = ContractStatus.Disabled; emit OperatorContractDisabled(operatorContract); } function isNewOperatorContract(address operatorContract) public view returns (bool) { return operatorContracts[operatorContract] == ContractStatus.New; } function isApprovedOperatorContract(address operatorContract) public view returns (bool) { return operatorContracts[operatorContract] == ContractStatus.Approved; } function operatorContractUpgraderFor(address _serviceContract) public view returns (address) { return operatorContractUpgraders[_serviceContract]; } function serviceContractUpgraderFor(address _operatorContract) public view returns (address) { return serviceContractUpgraders[_operatorContract]; } }