/** ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓ ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ Trust math, not hardware. */ pragma solidity 0.5.17; import "./KeepRegistry.sol"; /// @title AuthorityDelegator /// @notice An operator contract can delegate authority to other operator /// contracts by implementing the AuthorityDelegator interface. /// /// To delegate authority, /// the recipient of delegated authority must call `claimDelegatedAuthority`, /// specifying the contract it wants delegated authority from. /// The staking contract calls `delegator.__isRecognized(recipient)` /// and if the call returns `true`, /// the named delegator contract is set as the recipient's authority delegator. /// Any future checks of registry approval or per-operator authorization /// will transparently mirror the delegator's status. /// /// Authority can be delegated recursively; /// an operator contract receiving delegated authority /// can recognize other operator contracts as recipients of its authority. interface AuthorityDelegator { function __isRecognized(address delegatedAuthorityRecipient) external returns (bool); } /// @title AuthorityVerifier /// @notice An operator contract can delegate authority to other operator /// contracts. Entry in the registry is not updated and source contract remains /// listed there as authorized. This interface is a verifier that support verification /// of contract authorization in case of authority delegation from the source contract. interface AuthorityVerifier { /// @notice Returns true if the given operator contract has been approved /// for use. The function never reverts. function isApprovedOperatorContract(address _operatorContract) external view returns (bool); } contract Authorizations is AuthorityVerifier { // Authorized operator contracts. mapping(address => mapping(address => bool)) internal authorizations; // Granters of delegated authority to operator contracts. // E.g. keep factories granting delegated authority to keeps. // `delegatedAuthority[keep] = factory` mapping(address => address) internal delegatedAuthority; // Registry contract with a list of approved operator contracts and upgraders. KeepRegistry internal registry; modifier onlyApprovedOperatorContract(address operatorContract) { require( isApprovedOperatorContract(operatorContract), "Operator contract unapproved" ); _; } constructor(KeepRegistry _registry) public { registry = _registry; } /// @notice Gets the authorizer for the specified operator address. /// @return Authorizer address. function authorizerOf(address _operator) public view returns (address); /// @notice Authorizes operator contract to access staked token balance of /// the provided operator. Can only be executed by stake operator authorizer. /// Contracts using delegated authority /// cannot be authorized with `authorizeOperatorContract`. /// Instead, authorize `getAuthoritySource(_operatorContract)`. /// @param _operator address of stake operator. /// @param _operatorContract address of operator contract. function authorizeOperatorContract( address _operator, address _operatorContract ) public onlyApprovedOperatorContract(_operatorContract) { require( authorizerOf(_operator) == msg.sender, "Not operator authorizer" ); require( getAuthoritySource(_operatorContract) == _operatorContract, "Delegated authority used" ); authorizations[_operatorContract][_operator] = true; } /// @notice Checks if operator contract has access to the staked token balance of /// the provided operator. /// @param _operator address of stake operator. /// @param _operatorContract address of operator contract. function isAuthorizedForOperator( address _operator, address _operatorContract ) public view returns (bool) { return authorizations[getAuthoritySource(_operatorContract)][_operator]; } /// @notice Grant the sender the same authority as `delegatedAuthoritySource` /// @dev If `delegatedAuthoritySource` is an approved operator contract /// and recognizes the claimant, this relationship will be recorded in /// `delegatedAuthority`. Later, the claimant can slash, seize, place locks etc. /// on operators that have authorized the `delegatedAuthoritySource`. /// If the `delegatedAuthoritySource` is disabled with the panic button, /// any recipients of delegated authority from it will also be disabled. function claimDelegatedAuthority(address delegatedAuthoritySource) public onlyApprovedOperatorContract(delegatedAuthoritySource) { require( AuthorityDelegator(delegatedAuthoritySource).__isRecognized( msg.sender ), "Unrecognized claimant" ); delegatedAuthority[msg.sender] = delegatedAuthoritySource; } /// @notice Checks if the operator contract is authorized in the registry. /// If the contract uses delegated authority it checks authorization of the /// source contract. /// @param _operatorContract address of operator contract. /// @return True if operator contract is approved, false if operator contract /// has not been approved or if it was disabled by the panic button. function isApprovedOperatorContract(address _operatorContract) public view returns (bool) { return registry.isApprovedOperatorContract( getAuthoritySource(_operatorContract) ); } /// @notice Get the source of the operator contract's authority. /// If the contract uses delegated authority, /// returns the original source of the delegated authority. /// If the contract doesn't use delegated authority, /// returns the contract itself. /// Authorize `getAuthoritySource(operatorContract)` /// to grant `operatorContract` the authority to penalize an operator. function getAuthoritySource(address operatorContract) public view returns (address) { address delegatedAuthoritySource = delegatedAuthority[operatorContract]; if (delegatedAuthoritySource == address(0)) { return operatorContract; } return getAuthoritySource(delegatedAuthoritySource); } }