From 79b7e539216d0e575b1d87e617d9a842e55798e3 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Tue, 27 Aug 2024 11:50:25 +0000 Subject: [PATCH 01/18] instance no longer nft interceptor --- contracts/instance/Instance.sol | 36 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index 4b136fff7..f9f3e5155 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -36,12 +36,13 @@ contract Instance is InstanceStore internal _instanceStore; NftId [] internal _products; - modifier onlyChainNft() { - if(msg.sender != getRegistry().getChainNftAddress()) { - revert(); - } - _; - } + // TODO cleanup + // modifier onlyChainNft() { + // if(msg.sender != getRegistry().getChainNftAddress()) { + // revert(); + // } + // _; + // } function initialize( InstanceAdmin instanceAdmin, @@ -63,14 +64,14 @@ contract Instance is _instanceAdmin = instanceAdmin; // setup instance object info - __Registerable_init( - instanceAdmin.authority(), - address(registry), - registry.getNftId(), - INSTANCE(), - true, - initialOwner, - ""); + __Registerable_init({ + authority: instanceAdmin.authority(), + registry: address(registry), + parentNftId: registry.getNftId(), + objectType: INSTANCE(), + isInterceptor: false, + initialOwner: initialOwner, + data: ""}); // store instance supporting contracts _instanceStore = instanceStore; @@ -226,13 +227,6 @@ contract Instance is // _instanceAdmin.setTargetFunctionRoleByInstance(targetName, selectors, roleId); } - //--- ITransferInterceptor ----------------------------------------------// - - function nftTransferFrom(address from, address to, uint256 tokenId, address operator) external onlyChainNft { - // TODO refactor - // _instanceAdmin.transferInstanceOwnerRole(from, to); - } - //--- initial setup functions -------------------------------------------// From 7bbcf92d56632b98ba5f08affa782be1f41c13a8 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Tue, 27 Aug 2024 18:38:46 +0000 Subject: [PATCH 02/18] add custom role creation --- contracts/authorization/AccessAdmin.sol | 1 + contracts/instance/IInstance.sol | 6 +- contracts/instance/IInstanceService.sol | 10 ++ contracts/instance/Instance.sol | 20 ++-- contracts/instance/InstanceAdmin.sol | 44 +++++-- .../instance/InstanceAuthorizationV3.sol | 22 +++- contracts/instance/InstanceReader.sol | 23 +++- contracts/instance/InstanceService.sol | 38 ++++++ contracts/registry/ServiceAuthorizationV3.sol | 5 +- contracts/type/RoleId.sol | 3 + scripts/libs/instance.ts | 2 + .../authorization/InstanceAuthz.t.sol | 109 ++++++++++++++++++ 12 files changed, 255 insertions(+), 28 deletions(-) create mode 100644 test/instance/authorization/InstanceAuthz.t.sol diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index e96e2ad57..7ab4f2241 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -574,6 +574,7 @@ contract AccessAdmin is { // create role info info.createdAt = TimestampLib.blockTimestamp(); + info.pausedAt = TimestampLib.max(); _roleInfo[roleId] = info; // create role name info diff --git a/contracts/instance/IInstance.sol b/contracts/instance/IInstance.sol index 4b5f7822a..be96e819c 100644 --- a/contracts/instance/IInstance.sol +++ b/contracts/instance/IInstance.sol @@ -74,7 +74,11 @@ interface IInstance is ///--- authz ------------------------------------------------------------// - function createRole(string memory roleName, string memory adminName) external returns (RoleId roleId, RoleId admin); + /// @dev Creates a new custom role for the calling instance. + /// Custom roles are intended to be used for access control of custom components and its helper contracts. + /// Custom roles are not intended to be used as target roles for custom contracts. + function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId); + function grantRole(RoleId roleId, address account) external; function revokeRole(RoleId roleId, address account) external; diff --git a/contracts/instance/IInstanceService.sol b/contracts/instance/IInstanceService.sol index 9f927e275..83bc9005e 100644 --- a/contracts/instance/IInstanceService.sol +++ b/contracts/instance/IInstanceService.sol @@ -6,6 +6,7 @@ import {IInstance} from "./IInstance.sol"; import {IService} from "../shared/IService.sol"; import {NftId} from "../type/NftId.sol"; import {ObjectType} from "../type/ObjectType.sol"; +import {RoleId} from "../type/RoleId.sol"; import {Seconds} from "../type/Seconds.sol"; import {UFixed} from "../type/UFixed.sol"; import {VersionPart} from "../type/Version.sol"; @@ -53,6 +54,15 @@ interface IInstanceService is IService { event LogInstanceCloned(NftId instanceNftId, address instance); + /// @dev creates a new role for the calling instance. + function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId); + + /// @dev grants the specified role to the specified account for the calling instance. + function grantRole(RoleId roleId, address account) external; + + /// @dev revokes the specified role from the specified account for the calling instance. + function revokeRole(RoleId roleId, address account) external; + /// @dev Locks the complete instance, including all its components. function setInstanceLocked(bool locked) external; diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index f9f3e5155..b0773e867 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -36,14 +36,6 @@ contract Instance is InstanceStore internal _instanceStore; NftId [] internal _products; - // TODO cleanup - // modifier onlyChainNft() { - // if(msg.sender != getRegistry().getChainNftAddress()) { - // revert(); - // } - // _; - // } - function initialize( InstanceAdmin instanceAdmin, InstanceStore instanceStore, @@ -167,14 +159,18 @@ contract Instance is //--- Roles ------------------------------------------------------------// - function createRole(string memory roleName, string memory adminName) + /// @inheritdoc IInstance + function createRole( + string memory roleName, + RoleId adminRoleId, + uint32 maxMemberCount + ) external restricted() onlyOwner() - returns (RoleId roleId, RoleId admin) + returns (RoleId roleId) { - // TODO refactor - // (roleId, admin) = _instanceAdmin.createRole(roleName, adminName); + return _instanceService.createRole(roleName, adminRoleId, maxMemberCount); } function grantRole(RoleId roleId, address account) diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index 6c7c6d7cf..b44f5563c 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -14,6 +14,7 @@ import {ObjectType, INSTANCE, ORACLE} from "../type/ObjectType.sol"; import {RoleId, RoleIdLib} from "../type/RoleId.sol"; import {Str, StrLib} from "../type/String.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; +import {TimestampLib} from "../type/Timestamp.sol"; import {VersionPart, VersionPartLib} from "../type/Version.sol"; @@ -26,8 +27,6 @@ contract InstanceAdmin is string public constant BUNDLE_SET_TARGET_NAME = "BundleSet"; string public constant RISK_SET_TARGET_NAME = "RiskSet"; - uint64 public constant CUSTOM_ROLE_ID_MIN = 10000; // MUST be even - error ErrorInstanceAdminNotInstanceService(address caller); error ErrorInstanceAdminNotRegistered(address instance); @@ -95,7 +94,7 @@ contract InstanceAdmin is _instance = IInstance(instance); _authorization = IAuthorization(authorization); _components = 0; - _customIdNext = CUSTOM_ROLE_ID_MIN; + _customIdNext = 0; // link nft ownability to instance _linkToNftOwnable(instance); @@ -180,14 +179,27 @@ contract InstanceAdmin is instance); } - /// @dev Creates a custom role - // TODO implement - // function createRole() - // external - // restricted() - // { - - // } + /// @dev Creates a custom role. + function createRole( + string memory roleName, + RoleId adminRoleId, + uint32 maxMemberCount + ) + external + restricted() + returns (RoleId roleId) + { + roleId = RoleIdLib.toCustomRoleId(_customIdNext++); + _createRole( + roleId, + RoleInfo({ + adminRoleId: adminRoleId, + roleType: IAccess.RoleType.Custom, + maxMemberCount: maxMemberCount, + name: StrLib.toStr(roleName), + createdAt: TimestampLib.zero(), + pausedAt: TimestampLib.zero()})); + } /// @dev Grants the provided role to the specified account function grantRole( @@ -199,6 +211,16 @@ contract InstanceAdmin is _grantRoleToAccount(roleId, account); } + /// @dev Revokes the provided role from the specified account + function revokeRole( + RoleId roleId, + address account) + external + restricted() + { + _revokeRoleFromAccount(roleId, account); + } + function setInstanceLocked(bool locked) external diff --git a/contracts/instance/InstanceAuthorizationV3.sol b/contracts/instance/InstanceAuthorizationV3.sol index c999c7722..8231d4663 100644 --- a/contracts/instance/InstanceAuthorizationV3.sol +++ b/contracts/instance/InstanceAuthorizationV3.sol @@ -3,13 +3,14 @@ pragma solidity ^0.8.20; import {IAccess} from "../authorization/IAccess.sol"; +import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {Authorization} from "../authorization/Authorization.sol"; import {ACCOUNTING, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, APPLICATION, POLICY, CLAIM, BUNDLE, RISK} from "../../contracts/type/ObjectType.sol"; import {BundleSet} from "../instance/BundleSet.sol"; import {Instance} from "../instance/Instance.sol"; import {InstanceAdmin} from "../instance/InstanceAdmin.sol"; import {InstanceStore} from "../instance/InstanceStore.sol"; -import {PUBLIC_ROLE} from "../type/RoleId.sol"; +import {ADMIN_ROLE, INSTANCE_OWNER_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; import {RiskSet} from "../instance/RiskSet.sol"; @@ -18,6 +19,7 @@ contract InstanceAuthorizationV3 { string public constant INSTANCE_ROLE_NAME = "InstanceRole"; + string public constant INSTANCE_OWNER_ROLE_NAME = "InstanceOwnerRole"; string public constant INSTANCE_TARGET_NAME = "Instance"; string public constant INSTANCE_STORE_TARGET_NAME = "InstanceStore"; @@ -44,12 +46,24 @@ contract InstanceAuthorizationV3 _addServiceTargetWithRole(CLAIM()); } + function _setupRoles() + internal + override + { + _addRole( + INSTANCE_OWNER_ROLE(), + AccessAdminLib.toRole( + ADMIN_ROLE(), + RoleType.Custom, + 0, // max member count special case: instance nft owner is sole role owner + INSTANCE_OWNER_ROLE_NAME)); + } + function _setupTargets() internal override { // instance supporting targets - // _addGifContractTarget(INSTANCE_ADMIN_TARGET_NAME); _addTarget(INSTANCE_ADMIN_TARGET_NAME); _addTarget(INSTANCE_STORE_TARGET_NAME); _addTarget(BUNDLE_SET_TARGET_NAME); @@ -141,6 +155,10 @@ contract InstanceAuthorizationV3 // authorize component service role functions = _authorizeForTarget(INSTANCE_ADMIN_TARGET_NAME, getServiceRole(INSTANCE())); + _authorize(functions, InstanceAdmin.createRole.selector, "createRole"); + _authorize(functions, InstanceAdmin.grantRole.selector, "grantRole"); + _authorize(functions, InstanceAdmin.revokeRole.selector, "revokeRole"); + _authorize(functions, InstanceAdmin.setInstanceLocked.selector, "setInstanceLocked"); _authorize(functions, InstanceAdmin.setTargetLocked.selector, "setTargetLocked"); diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index ec5c70c74..246dab2df 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IAccess} from "../authorization/IAccess.sol"; import {IBundle} from "../instance/module/IBundle.sol"; import {IComponents} from "../instance/module/IComponents.sol"; import {IDistribution} from "../instance/module/IDistribution.sol"; @@ -27,7 +28,7 @@ import {ReferralId, ReferralStatus, ReferralLib, REFERRAL_OK, REFERRAL_ERROR_UNK import {RequestId} from "../type/RequestId.sol"; import {RiskId} from "../type/RiskId.sol"; import {RiskSet} from "./RiskSet.sol"; -import {RoleId} from "../type/RoleId.sol"; +import {RoleId, INSTANCE_OWNER_ROLE} from "../type/RoleId.sol"; import {StateId} from "../type/StateId.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; import {UFixed, UFixedLib} from "../type/UFixed.sol"; @@ -563,6 +564,26 @@ contract InstanceReader { } + function roles() public view returns (uint256) { + return _instance.getInstanceAdmin().roles(); + } + + + function getInstanceOwnerRole() public view returns (RoleId roleId) { + return INSTANCE_OWNER_ROLE(); + } + + + function getRoleId(uint256 idx) public view returns (RoleId roleId) { + return _instance.getInstanceAdmin().getRoleId(uint64(idx)); + } + + + function getRoleInfo(RoleId roleId) public view returns (IAccess.RoleInfo memory roleInfo) { + return _instance.getInstanceAdmin().getRoleInfo(roleId); + } + + function hasRole(address account, RoleId roleId) public view returns (bool isMember) { return _instance.getInstanceAdmin().hasRole(account, roleId); } diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index abf8fa99e..f2a7a9f7c 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -72,6 +72,44 @@ contract InstanceService is _; } + /// @inheritdoc IInstanceService + function createRole( + string memory roleName, + RoleId adminRoleId, + uint32 maxMemberCount + ) + external + restricted() + onlyInstance() + returns (RoleId roleId) + { + IInstance instance = IInstance(msg.sender); + roleId = instance.getInstanceAdmin().createRole( + roleName, + adminRoleId, + maxMemberCount); + } + + /// @inheritdoc IInstanceService + function grantRole(RoleId roleId, address account) + external + restricted() + onlyInstance() + { + IInstance instance = IInstance(msg.sender); + instance.getInstanceAdmin().grantRole(roleId, account); + } + + /// @inheritdoc IInstanceService + function revokeRole(RoleId roleId, address account) + external + restricted() + onlyInstance() + { + IInstance instance = IInstance(msg.sender); + instance.getInstanceAdmin().revokeRole(roleId, account); + } + /// @inheritdoc IInstanceService function setInstanceLocked(bool locked) external diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index 32bc7f410..7ba91248e 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -58,7 +58,6 @@ contract ServiceAuthorizationV3 _setupIRegistryServiceAuthorization(); _setupStakingServiceAuthorization(); _setupInstanceServiceAuthorization(); - _setupInstanceServiceAuthorization(); _setupAccountingServiceAuthorization(); _setupComponentServiceAuthorization(); _setupClaimServiceAuthorization(); @@ -130,6 +129,10 @@ contract ServiceAuthorizationV3 { IAccess.FunctionInfo[] storage functions; functions = _authorizeForService(INSTANCE(), ALL()); + _authorize(functions, IInstanceService.createRole.selector, "createRole"); + _authorize(functions, IInstanceService.grantRole.selector, "grantRole"); + _authorize(functions, IInstanceService.revokeRole.selector, "revokeRole"); + _authorize(functions, IInstanceService.setInstanceLocked.selector, "setInstanceLocked"); _authorize(functions, IInstanceService.setTargetLocked.selector, "setTargetLocked"); diff --git a/contracts/type/RoleId.sol b/contracts/type/RoleId.sol index 7ca224bdf..6f0f09855 100644 --- a/contracts/type/RoleId.sol +++ b/contracts/type/RoleId.sol @@ -59,6 +59,9 @@ function GIF_REMOTE_MANAGER_ROLE() pure returns (RoleId) { return RoleIdLib.toRo /// @dev role assigned to release registry, release specfic to lock/unlock a release function RELEASE_REGISTRY_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(4); } +/// @dev role assigned to every instance owner +function INSTANCE_OWNER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(5); } + //--- GIF core contract roles (range: 200 - 9'900) --------------------------// // created and assigned during initial deployment for registry and staking // granting for instances and components in instance service diff --git a/scripts/libs/instance.ts b/scripts/libs/instance.ts index c5f292c2d..d84d5c2e9 100644 --- a/scripts/libs/instance.ts +++ b/scripts/libs/instance.ts @@ -44,6 +44,7 @@ export async function deployAndRegisterMasterInstance( [], { libraries: { + AccessAdminLib: libraries.accessAdminLibAddress, ObjectTypeLib: libraries.objectTypeLibAddress, RoleIdLib: libraries.roleIdLibAddress, SelectorLib: libraries.selectorLibAddress, @@ -166,6 +167,7 @@ export async function deployAndRegisterMasterInstance( ReferralLib: libraries.referralLibAddress, RequestIdLib: libraries.requestIdLibAddress, RiskIdLib: libraries.riskIdLibAddress, + RoleIdLib: libraries.roleIdLibAddress, TimestampLib: libraries.timestampLibAddress, UFixedLib: libraries.uFixedLibAddress, } diff --git a/test/instance/authorization/InstanceAuthz.t.sol b/test/instance/authorization/InstanceAuthz.t.sol new file mode 100644 index 000000000..4af99acd7 --- /dev/null +++ b/test/instance/authorization/InstanceAuthz.t.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {console} from "../../../lib/forge-std/src/Test.sol"; + +import {IAccess} from "../../../contracts/authorization/IAccess.sol"; +import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; + +import {ComponentService} from "../../../contracts/shared/ComponentService.sol"; +import {GifTest} from "../../base/GifTest.sol"; +import {FeeLib} from "../../../contracts/type/Fee.sol"; +import {NftId, NftIdLib} from "../../../contracts/type/NftId.sol"; +import {RoleId,RoleIdLib, ADMIN_ROLE, INSTANCE_OWNER_ROLE} from "../../../contracts/type/RoleId.sol"; +import {SimplePool} from "../../../contracts/examples/unpermissioned/SimplePool.sol"; +import {SimpleProduct} from "../../../contracts/examples/unpermissioned/SimpleProduct.sol"; +import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; +import {UFixedLib} from "../../../contracts/type/UFixed.sol"; + +contract InstanceAuthorizationTest is GifTest { + + function test_instanceAuthorizationSetup() public { + _printRoles(); + + // check initial roles + assertEq(instanceAdmin.roles(), 15, "unexpected initial instance roles count (admin)"); + assertEq(instanceReader.roles(), 15, "unexpected initial instance roles count (reader)"); + } + + function test_instanceAuthorizationCreateRoleHappyCase() public { + // GIVEN setup + uint256 rolesBefore = instanceReader.roles(); + string memory roleName = "MyCustomRole"; + RoleId adminRole = instanceReader.getInstanceOwnerRole(); + uint32 maxMemberCount = 42; + + // WHEN + vm.prank(instanceOwner); + RoleId myCustomRoleId = instance.createRole( + roleName, + adminRole, + maxMemberCount); + + // THEN + assertTrue(myCustomRoleId.gtz(), "role id zero"); + assertEq(instanceReader.roles(), rolesBefore + 1, "unexpected roles count after createRole"); + + IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(myCustomRoleId); + assertEq(roleInfo.adminRoleId.toInt(), INSTANCE_OWNER_ROLE().toInt(), "instance owner role not role admin"); + assertTrue(roleInfo.roleType == IAccess.RoleType.Custom, "not custom role type"); + assertEq(roleInfo.maxMemberCount, maxMemberCount, "unexpected max member count"); + assertEq(roleInfo.name.toString(), "MyCustomRole", "unexpected role name"); + assertEq(roleInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected created at timestamp"); + assertEq(roleInfo.pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); + } + + + function test_instanceAuthorizationCreateRoleRoleNameEmpty() public { + // GIVEN setup + string memory roleName = ""; + RoleId adminRole = instanceReader.getInstanceOwnerRole(); + uint32 maxMemberCount = 42; + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IAccessAdmin.ErrorAccessAdminRoleNameEmpty.selector, + 1000000)); + + vm.prank(instanceOwner); + RoleId myCustomRoleId = instance.createRole( + roleName, + adminRole, + maxMemberCount); + + assertTrue(myCustomRoleId.eqz(), "role id not zero"); + } + + + function test_instanceAuthorizationCreateRoleAdminRoleNonexistent() public { + // GIVEN setup + string memory roleName = "myCustomRole"; + RoleId adminRole = RoleIdLib.toRoleId(12345); + uint32 maxMemberCount = 42; + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IAccessAdmin.ErrorAccessAdminRoleAdminNotExisting.selector, + adminRole)); + + vm.prank(instanceOwner); + RoleId myCustomRoleId = instance.createRole( + roleName, + adminRole, + maxMemberCount); + + assertTrue(myCustomRoleId.eqz(), "role id not zero"); + } + + + function _printRoles() internal { + // print roles + for(uint256 i = 0; i < instanceReader.roles(); i++) { + RoleId roleId = instanceReader.getRoleId(i); + IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(roleId); + console.log("role", i, roleId.toInt(), roleInfo.name.toString()); + } + } +} \ No newline at end of file From ad0d5ed97cb8171a9ee41665a9d4dc2739312375 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Wed, 28 Aug 2024 12:53:11 +0000 Subject: [PATCH 03/18] add custom role (de)activation --- contracts/authorization/AccessAdmin.sol | 214 ++++++++-------- contracts/authorization/AccessAdminLib.sol | 28 ++- contracts/authorization/IAccessAdmin.sol | 9 +- contracts/instance/IInstance.sol | 14 ++ contracts/instance/IInstanceService.sol | 3 + contracts/instance/Instance.sol | 48 +++- contracts/instance/InstanceAdmin.sol | 26 +- .../instance/InstanceAuthorizationV3.sol | 2 + contracts/instance/InstanceReader.sol | 21 +- contracts/instance/InstanceService.sol | 11 + contracts/registry/RegistryAdmin.sol | 3 +- contracts/registry/ReleaseAdmin.sol | 5 +- contracts/registry/ServiceAuthorizationV3.sol | 1 + test/GifDeployer.t.sol | 4 +- test/authorization/AccessAdmin.t.sol | 234 +++--------------- .../authorization/AccessAdminManageMock.t.sol | 24 +- test/authorization/RegistryAdminEx.sol | 2 +- .../authorization/InstanceAuthz.t.sol | 185 +++++++++++++- test/release/ReleaseRegistryConcrete.t.sol | 12 +- 19 files changed, 491 insertions(+), 355 deletions(-) diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index 7ab4f2241..532d240fd 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -86,6 +86,7 @@ contract AccessAdmin is /// @dev temporary dynamic functions array bytes4[] private _functions; + modifier onlyDeployer() { // special case for cloned AccessAdmin contracts // IMPORTANT cloning and initialize authority needs to be done in a single transaction @@ -99,23 +100,6 @@ contract AccessAdmin is _; } - modifier onlyRoleMember(RoleId roleId, address account) { - _checkRoleExists(roleId, false); - - if (!hasRole(account, roleId)) { - revert ErrorAccessAdminNotRoleOwner(roleId, account); - } - _; - } - - modifier onlyRoleAdmin(RoleId roleId, address account) { - _checkRoleExists(roleId, false); - - if (!hasAdminRole(account, roleId)) { - revert ErrorAccessAdminNotAdminOfRole(_roleInfo[roleId].adminRoleId, account); - } - _; - } modifier onlyExistingRole( RoleId roleId, @@ -129,6 +113,7 @@ contract AccessAdmin is _; } + modifier onlyExistingTarget(address target) { _checkTargetExists(target); _; @@ -253,6 +238,14 @@ contract AccessAdmin is return _roleInfo[roleId]; } + function isRoleActive(RoleId roleId) external view returns (bool isActive) { + return _roleInfo[roleId].pausedAt > TimestampLib.blockTimestamp(); + } + + function isRoleCustom(RoleId roleId) external view returns (bool isActive) { + return _roleInfo[roleId].roleType == RoleType.Custom; + } + function roleMembers(RoleId roleId) external view returns (uint256 numberOfMembers) { return _roleMembers[roleId].length(); } @@ -262,20 +255,20 @@ contract AccessAdmin is } // TODO false because not role member or because role not exists? - function hasRole(address account, RoleId roleId) public view returns (bool) { + function isRoleMember(address account, RoleId roleId) public view returns (bool) { (bool isMember, ) = _authority.hasRole( RoleId.unwrap(roleId), account); return isMember; } - function hasAdminRole(address account, RoleId roleId) + function isRoleAdmin(address account, RoleId roleId) public virtual view returns (bool) { - return hasRole(account, _roleInfo[roleId].adminRoleId); + return isRoleMember(account, _roleInfo[roleId].adminRoleId); } //--- view functions for targets ----------------------------------------// @@ -384,110 +377,122 @@ contract AccessAdmin is function _authorizeTargetFunctions( address target, RoleId roleId, - FunctionInfo[] memory functions + FunctionInfo[] memory functions, + bool addFunctions ) internal { - if (roleId == getAdminRole()) { + if (addFunctions && roleId == getAdminRole()) { revert ErrorAccessAdminAuthorizeForAdminRoleInvalid(target); } - ( - bytes4[] memory functionSelectors, - string[] memory functionNames - ) = _processFunctionSelectors(target, functions, true); - // apply authz via access manager _grantRoleAccessToFunctions( target, roleId, - functionSelectors, - functionNames, - false); // allow locked roles - } - - function _unauthorizeTargetFunctions( - address target, - FunctionInfo[] memory functions - ) - internal - { - ( - bytes4[] memory functionSelectors, - string[] memory functionNames - ) = _processFunctionSelectors(target, functions, false); + functions, + addFunctions); // add functions + } + + // function _unauthorizeTargetFunctions( + // address target, + // FunctionInfo[] memory functions + // ) + // internal + // { + // _grantRoleAccessToFunctions( + // target, + // getAdminRole(), + // functions, + // false); // addFunctions + // } + + // function _processFunctionSelectors( + // address target, + // FunctionInfo[] memory functions, + // bool addFunctions + // ) + // internal + // onlyExistingTarget(target) + // returns ( + // bytes4[] memory functionSelectors, + // string[] memory functionNames + // ) + // { + // uint256 n = functions.length; + // functionSelectors = new bytes4[](n); + // functionNames = new string[](n); + // FunctionInfo memory func; + // Selector selector; + + // for (uint256 i = 0; i < n; i++) { + // func = functions[i]; + // selector = func.selector; + + // // add function selector to target selector set if not in set + // if (addFunctions) { SelectorSetLib.add(_targetFunctions[target], selector); } + // else { SelectorSetLib.remove(_targetFunctions[target], selector); } + + // // set function name + // _functionInfo[target][selector] = func; + + // // add bytes4 selector to function selector array + // functionSelectors[i] = selector.toBytes4(); + // functionNames[i] = func.name.toString(); + // } + // } - _grantRoleAccessToFunctions( - target, - getAdminRole(), - functionSelectors, - functionNames, - true); // allowLockedRoles - } - - function _processFunctionSelectors( + /// @dev grant the specified role access to all functions in the provided selector list + function _grantRoleAccessToFunctions( address target, + RoleId roleId, FunctionInfo[] memory functions, bool addFunctions ) internal onlyExistingTarget(target) - returns ( - bytes4[] memory functionSelectors, - string[] memory functionNames - ) + onlyExistingRole(roleId, true, !addFunctions) { - uint256 n = functions.length; - functionSelectors = new bytes4[](n); - functionNames = new string[](n); - FunctionInfo memory func; - Selector selector; - - for (uint256 i = 0; i < n; i++) { - func = functions[i]; - selector = func.selector; - - // add function selector to target selector set if not in set - if (addFunctions) { SelectorSetLib.add(_targetFunctions[target], selector); } - else { SelectorSetLib.remove(_targetFunctions[target], selector); } - - // set function name - _functionInfo[target][selector] = func; - - // add bytes4 selector to function selector array - functionSelectors[i] = selector.toBytes4(); - functionNames[i] = func.name.toString(); + _authority.setTargetFunctionRole( + target, + AccessAdminLib.getSelectors(functions), + RoleId.unwrap(roleId)); + + // update function set and log function grantings + for (uint256 i = 0; i < functions.length; i++) { + _updateFunctionAccess( + target, + roleId, + functions[i], + addFunctions); } } - /// @dev grant the specified role access to all functions in the provided selector list - function _grantRoleAccessToFunctions( - address target, - RoleId roleId, - bytes4[] memory functionSelectors, - string[] memory functionNames, - bool allowLockedRoles // admin and public roles are locked + + function _updateFunctionAccess( + address target, + RoleId roleId, + FunctionInfo memory func, + bool addFunction ) internal - onlyExistingTarget(target) - onlyExistingRole(roleId, true, allowLockedRoles) { + // update functions info + Selector selector = func.selector; + _functionInfo[target][selector] = func; - _authority.setTargetFunctionRole( - target, - functionSelectors, - RoleId.unwrap(roleId)); + // update function sets + if (addFunction) { SelectorSetLib.add(_targetFunctions[target], selector); } + else { SelectorSetLib.remove(_targetFunctions[target], selector); } - // log function grantings - for (uint256 i = 0; i < functionSelectors.length; i++) { - emit LogAccessAdminFunctionGranted( - _adminName, - target, - string(abi.encodePacked( - functionNames[i], - "(): ", - _getRoleName(roleId)))); - } + // logging + emit LogAccessAdminFunctionGranted( + _adminName, + target, + string(abi.encodePacked( + func.name.toString(), + "(): ", + _getRoleName(roleId)))); } @@ -553,6 +558,19 @@ contract AccessAdmin is } + /// @dev Activates or deactivates role. + /// The role activ property is indirectly controlled over the pausedAt timestamp. + function _setRoleActive(RoleId roleId, bool active) + internal + { + if (active) { + _roleInfo[roleId].pausedAt = TimestampLib.max(); + } else { + _roleInfo[roleId].pausedAt = TimestampLib.blockTimestamp(); + } + } + + function _createTarget( address target, string memory targetName, diff --git a/contracts/authorization/AccessAdminLib.sol b/contracts/authorization/AccessAdminLib.sol index a647ae0dc..e4a351f17 100644 --- a/contracts/authorization/AccessAdminLib.sol +++ b/contracts/authorization/AccessAdminLib.sol @@ -19,6 +19,22 @@ import {VersionPart} from "../type/Version.sol"; library AccessAdminLib { // ACCESS_ADMIN_LIB + + function getSelectors( + IAccess.FunctionInfo[] memory functions + ) + public + returns ( + bytes4[] memory selectors + ) + { + uint256 n = functions.length; + selectors = new bytes4[](n); + for (uint256 i = 0; i < n; i++) { + selectors[i] = functions[i].selector.toBytes4(); + } + } + function checkRoleCreation( IAccessAdmin accessAdmin, RoleId roleId, @@ -154,7 +170,12 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB } } - function toRole(RoleId adminRoleId, IAccessAdmin.RoleType roleType, uint32 maxMemberCount, string memory name) + function toRole( + RoleId adminRoleId, + IAccessAdmin.RoleType roleType, + uint32 maxMemberCount, + string memory name + ) public view returns (IAccess.RoleInfo memory) @@ -169,7 +190,10 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB }); } - function toFunction(bytes4 selector, string memory name) + function toFunction( + bytes4 selector, + string memory name + ) public view returns (IAccess.FunctionInfo memory) diff --git a/contracts/authorization/IAccessAdmin.sol b/contracts/authorization/IAccessAdmin.sol index 70773f578..0ea7fe586 100644 --- a/contracts/authorization/IAccessAdmin.sol +++ b/contracts/authorization/IAccessAdmin.sol @@ -39,6 +39,9 @@ interface IAccessAdmin is // only role owner modifier error ErrorAccessAdminNotRoleOwner(RoleId roleId, address account); + // only custom role modifier + error ErrorAccessAdminRoleNotCustom(RoleId roleId); + // initialization error ErrorAccessAdminNotRegistry(address registry); error ErrorAccessAdminAuthorityNotContract(address authority); @@ -150,9 +153,11 @@ interface IAccessAdmin is function roleExists(RoleId roleId) external view returns (bool exists); function getRoleForName(string memory name) external view returns (RoleId roleId); function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory roleInfo); + function isRoleActive(RoleId roleId) external view returns (bool isActive); + function isRoleCustom(RoleId roleId) external view returns (bool isCustom); - function hasRole(address account, RoleId roleId) external view returns (bool); - function hasAdminRole(address account, RoleId roleId) external view returns (bool); + function isRoleMember(address account, RoleId roleId) external view returns (bool); + function isRoleAdmin(address account, RoleId roleId) external view returns (bool); function roleMembers(RoleId roleId) external view returns (uint256 numberOfMembers); function getRoleMember(RoleId roleId, uint256 idx) external view returns (address account); diff --git a/contracts/instance/IInstance.sol b/contracts/instance/IInstance.sol index be96e819c..02f2aa3da 100644 --- a/contracts/instance/IInstance.sol +++ b/contracts/instance/IInstance.sol @@ -17,6 +17,10 @@ import {UFixed} from "../type/UFixed.sol"; interface IInstance is IRegisterable { + // modifier is onlyRoleAdmin + error ErrorInstanceNotCustomRole(RoleId roleId); + error ErrorInstanceNotRoleAdmin(RoleId roleId, address account); + error ErrorInstanceInstanceAdminZero(); error ErrorInstanceInstanceAdminAlreadySet(address InstanceAdmin); error ErrorInstanceInstanceAdminAuthorityMismatch(address instanceAuthority); @@ -79,7 +83,17 @@ interface IInstance is /// Custom roles are not intended to be used as target roles for custom contracts. function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId); + /// @dev Activates/deactivates the specified role. + /// Only instance owner or account with role admin role can call this function. + function setRoleActive(RoleId roleId, bool active) external; + + /// @dev Grants the specified role to the account. + /// Only active roles can be granted. + /// Only instance owner or account with role admin role can call this function. function grantRole(RoleId roleId, address account) external; + + /// @dev Revokes the specified role from the account. + /// Only instance owner or account with role admin role can call this function. function revokeRole(RoleId roleId, address account) external; function createTarget(address target, string memory name) external; diff --git a/contracts/instance/IInstanceService.sol b/contracts/instance/IInstanceService.sol index 83bc9005e..7e41922c7 100644 --- a/contracts/instance/IInstanceService.sol +++ b/contracts/instance/IInstanceService.sol @@ -57,6 +57,9 @@ interface IInstanceService is IService { /// @dev creates a new role for the calling instance. function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId); + /// @dev sets the specified role as active or inactive for the calling instance. + function setRoleActive(RoleId roleId, bool active) external; + /// @dev grants the specified role to the specified account for the calling instance. function grantRole(RoleId roleId, address account) external; diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index b0773e867..5e0e8b84f 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -36,6 +36,23 @@ contract Instance is InstanceStore internal _instanceStore; NftId [] internal _products; + + modifier onlyRoleAdmin(RoleId roleId) { + if (!_instanceAdmin.isRoleCustom(roleId)) { + revert ErrorInstanceNotCustomRole(roleId); + } + + // instance owner can always act as role admin + address account = msg.sender; + if (account != getOwner()) { + if (!_instanceAdmin.isRoleAdmin(account, roleId)) { + revert ErrorInstanceNotRoleAdmin(roleId, account); + } + } + _; + } + + function initialize( InstanceAdmin instanceAdmin, InstanceStore instanceStore, @@ -173,21 +190,34 @@ contract Instance is return _instanceService.createRole(roleName, adminRoleId, maxMemberCount); } + + /// @inheritdoc IInstance + function setRoleActive(RoleId roleId, bool active) + external + restricted() + onlyRoleAdmin(roleId) + { + _instanceService.setRoleActive(roleId, active); + } + + + /// @inheritdoc IInstance function grantRole(RoleId roleId, address account) external restricted() - onlyOwner() + onlyRoleAdmin(roleId) { - _instanceAdmin.grantRole(roleId, account); + _instanceService.grantRole(roleId, account); } + + /// @inheritdoc IInstance function revokeRole(RoleId roleId, address account) external restricted() - onlyOwner() + onlyRoleAdmin(roleId) { - // TODO refactor - // AccessManagerExtendedInitializeable(authority()).revokeRole(roleId.toInt(), account); + _instanceService.grantRole(roleId, account); } //--- Targets ------------------------------------------------------------// @@ -276,4 +306,12 @@ contract Instance is } //--- internal view/pure functions --------------------------------------// + + + function _checkCustomRole(RoleId roleId, bool exists) internal view { + if (!_instanceAdmin.isRoleCustom(roleId)) { + revert ErrorInstanceNotCustomRole(roleId); + } + } + } \ No newline at end of file diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index b44f5563c..ff2101cb1 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -8,6 +8,7 @@ import {IRegistry} from "../registry/IRegistry.sol"; import {IInstance} from "./IInstance.sol"; import {AccessAdmin} from "../authorization/AccessAdmin.sol"; +import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; import {NftId} from "../type/NftId.sol"; import {ObjectType, INSTANCE, ORACLE} from "../type/ObjectType.sol"; @@ -192,15 +193,23 @@ contract InstanceAdmin is roleId = RoleIdLib.toCustomRoleId(_customIdNext++); _createRole( roleId, - RoleInfo({ - adminRoleId: adminRoleId, - roleType: IAccess.RoleType.Custom, - maxMemberCount: maxMemberCount, - name: StrLib.toStr(roleName), - createdAt: TimestampLib.zero(), - pausedAt: TimestampLib.zero()})); + AccessAdminLib.toRole( + adminRoleId, + IAccess.RoleType.Custom, + maxMemberCount, + roleName)); } + + /// @dev Activtes/pauses the specified role. + function setRoleActive(RoleId roleId, bool active) + external + restricted() + { + _setRoleActive(roleId, active); + } + + /// @dev Grants the provided role to the specified account function grantRole( RoleId roleId, @@ -415,7 +424,8 @@ contract InstanceAdmin is authorizedRole, authorization.getAuthorizedFunctions( target, - authorizedRole)); + authorizedRole), + true); } } } diff --git a/contracts/instance/InstanceAuthorizationV3.sol b/contracts/instance/InstanceAuthorizationV3.sol index 8231d4663..a7d2fe27b 100644 --- a/contracts/instance/InstanceAuthorizationV3.sol +++ b/contracts/instance/InstanceAuthorizationV3.sol @@ -137,6 +137,7 @@ contract InstanceAuthorizationV3 // custom authz _authorize(functions, Instance.createRole.selector, "createRole"); + _authorize(functions, Instance.setRoleActive.selector, "setRoleActive"); _authorize(functions, Instance.grantRole.selector, "grantRole"); _authorize(functions, Instance.revokeRole.selector, "revokeRole"); _authorize(functions, Instance.createTarget.selector, "createTarget"); @@ -156,6 +157,7 @@ contract InstanceAuthorizationV3 // authorize component service role functions = _authorizeForTarget(INSTANCE_ADMIN_TARGET_NAME, getServiceRole(INSTANCE())); _authorize(functions, InstanceAdmin.createRole.selector, "createRole"); + _authorize(functions, InstanceAdmin.setRoleActive.selector, "setRoleActive"); _authorize(functions, InstanceAdmin.grantRole.selector, "grantRole"); _authorize(functions, InstanceAdmin.revokeRole.selector, "revokeRole"); diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index 246dab2df..782ef7376 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -578,19 +578,32 @@ contract InstanceReader { return _instance.getInstanceAdmin().getRoleId(uint64(idx)); } + function roleExists(RoleId roleId) public view returns (bool exists) { + return _instance.getInstanceAdmin().roleExists(roleId); + } function getRoleInfo(RoleId roleId) public view returns (IAccess.RoleInfo memory roleInfo) { return _instance.getInstanceAdmin().getRoleInfo(roleId); } - function hasRole(address account, RoleId roleId) public view returns (bool isMember) { - return _instance.getInstanceAdmin().hasRole(account, roleId); + function isRoleCustom(RoleId roleId) public view returns (bool isCustom) { + return _instance.getInstanceAdmin().isRoleCustom(roleId); + } + + + function isRoleActive(RoleId roleId) public view returns (bool isActive) { + return _instance.getInstanceAdmin().isRoleActive(roleId); + } + + + function isRoleMember(address account, RoleId roleId) public view returns (bool isMember) { + return _instance.getInstanceAdmin().isRoleMember(account, roleId); } - function hasAdminRole(address account, RoleId roleId) public view returns (bool isMember) { - return _instance.getInstanceAdmin().hasAdminRole(account, roleId); + function isRoleAdmin(address account, RoleId roleId) public view returns (bool isMember) { + return _instance.getInstanceAdmin().isRoleAdmin(account, roleId); } diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index f2a7a9f7c..311375215 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -90,6 +90,17 @@ contract InstanceService is maxMemberCount); } + /// @inheritdoc IInstanceService + function setRoleActive(RoleId roleId, bool active) + external + restricted() + onlyInstance() + { + IInstance instance = IInstance(msg.sender); + instance.getInstanceAdmin().setRoleActive(roleId, active); + } + + /// @inheritdoc IInstanceService function grantRole(RoleId roleId, address account) external diff --git a/contracts/registry/RegistryAdmin.sol b/contracts/registry/RegistryAdmin.sol index e5ab042dd..99a25ec89 100644 --- a/contracts/registry/RegistryAdmin.sol +++ b/contracts/registry/RegistryAdmin.sol @@ -234,7 +234,8 @@ contract RegistryAdmin is authorizedRole, authorization.getAuthorizedFunctions( target, - authorizedRole)); + authorizedRole), + true); } } } diff --git a/contracts/registry/ReleaseAdmin.sol b/contracts/registry/ReleaseAdmin.sol index bfe9cc21b..503c11f05 100644 --- a/contracts/registry/ReleaseAdmin.sol +++ b/contracts/registry/ReleaseAdmin.sol @@ -222,7 +222,8 @@ contract ReleaseAdmin is _authorizeTargetFunctions( address(service), authorizedRoleId, - authorizatedFunctions); + authorizatedFunctions, + true); } } @@ -246,7 +247,7 @@ contract ReleaseAdmin is functions = new FunctionInfo[](2); functions[0] = AccessAdminLib.toFunction(ReleaseAdmin.authorizeService.selector, "authorizeService"); functions[1] = AccessAdminLib.toFunction(ReleaseAdmin.setServiceLocked.selector, "setServiceLocked"); - _authorizeTargetFunctions(address(this), RELEASE_REGISTRY_ROLE(), functions); + _authorizeTargetFunctions(address(this), RELEASE_REGISTRY_ROLE(), functions, true); _grantRoleToAccount(RELEASE_REGISTRY_ROLE(), releaseRegistry); } diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index 7ba91248e..f367fcbcb 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -130,6 +130,7 @@ contract ServiceAuthorizationV3 IAccess.FunctionInfo[] storage functions; functions = _authorizeForService(INSTANCE(), ALL()); _authorize(functions, IInstanceService.createRole.selector, "createRole"); + _authorize(functions, IInstanceService.setRoleActive.selector, "setRoleActive"); _authorize(functions, IInstanceService.grantRole.selector, "grantRole"); _authorize(functions, IInstanceService.revokeRole.selector, "revokeRole"); diff --git a/test/GifDeployer.t.sol b/test/GifDeployer.t.sol index 4d403fa37..9d28ceea7 100644 --- a/test/GifDeployer.t.sol +++ b/test/GifDeployer.t.sol @@ -115,8 +115,8 @@ contract GifDeployerTest is GifDeployer { assertEq(registryAdmin.authority(), registryAdmin.authority(), "unexpected release manager authority"); // check initial roles assignments - assertTrue(registryAdmin.hasRole(gifAdmin, GIF_ADMIN_ROLE()), "registry owner not admin"); - assertTrue(registryAdmin.hasRole(gifManager, GIF_MANAGER_ROLE()), "registry owner not manager"); + assertTrue(registryAdmin.isRoleMember(gifAdmin, GIF_ADMIN_ROLE()), "registry owner not admin"); + assertTrue(registryAdmin.isRoleMember(gifManager, GIF_MANAGER_ROLE()), "registry owner not manager"); // check sample admin access IAccessManager authority = IAccessManager(registryAdmin.authority()); diff --git a/test/authorization/AccessAdmin.t.sol b/test/authorization/AccessAdmin.t.sol index f23e88368..0a1829d38 100644 --- a/test/authorization/AccessAdmin.t.sol +++ b/test/authorization/AccessAdmin.t.sol @@ -70,7 +70,7 @@ contract AccessAdminForTesting is AccessAdmin { functions[1] = AccessAdminLib.toFunction(AccessAdminForTesting.grantRole.selector, "grantRole"); functions[2] = AccessAdminLib.toFunction(AccessAdminForTesting.revokeRole.selector, "revokeRole"); functions[3] = AccessAdminLib.toFunction(AccessAdminForTesting.renounceRole.selector, "renounceRole"); - _authorizeTargetFunctions(address(this), getPublicRole(), functions); + _authorizeTargetFunctions(address(this), getPublicRole(), functions, true); // grant manager role access to the specified functions functions = new FunctionInfo[](6); @@ -80,7 +80,7 @@ contract AccessAdminForTesting is AccessAdmin { functions[3] = AccessAdminLib.toFunction(AccessAdminForTesting.setTargetLocked.selector, "setTargetLocked"); functions[4] = AccessAdminLib.toFunction(AccessAdminForTesting.authorizeFunctions.selector, "authorizeFunctions"); functions[5] = AccessAdminLib.toFunction(AccessAdminForTesting.unauthorizeFunctions.selector, "unauthorizeFunctions"); - _authorizeTargetFunctions(address(this), getManagerRole(), functions); + _authorizeTargetFunctions(address(this), getManagerRole(), functions, true); _grantRoleToAccount(_managerRoleId, _deployer); } @@ -137,7 +137,6 @@ contract AccessAdminForTesting is AccessAdmin { ) external virtual - onlyRoleAdmin(roleId, msg.sender) restricted() { _grantRoleToAccount(roleId, account); @@ -150,7 +149,6 @@ contract AccessAdminForTesting is AccessAdmin { external virtual restricted() - onlyRoleAdmin(roleId, msg.sender) { _revokeRoleFromAccount(roleId, account); } @@ -161,7 +159,6 @@ contract AccessAdminForTesting is AccessAdmin { external virtual restricted() - onlyRoleMember(roleId, msg.sender) { _revokeRoleFromAccount(roleId, msg.sender); } @@ -190,7 +187,7 @@ contract AccessAdminForTesting is AccessAdmin { virtual restricted() { - _authorizeTargetFunctions(target, roleId, functions); + _authorizeTargetFunctions(target, roleId, functions, true); } function unauthorizeFunctions( @@ -201,7 +198,7 @@ contract AccessAdminForTesting is AccessAdmin { virtual restricted() { - _unauthorizeTargetFunctions(target, functions); + _authorizeTargetFunctions(target, getAdminRole(), functions, false); } @@ -299,8 +296,8 @@ contract AccessAdminTest is AccessAdminBaseTest { // WHEN (empty) // THEN RoleId adminRole = accessAdmin.getAdminRole(); - assertTrue(accessAdmin.hasRole(address(accessAdmin), adminRole), "access admin contract does not have admin role"); - assertFalse(accessAdmin.hasRole(accessAdminDeployer, adminRole), "access admin deployer does have admin role"); + assertTrue(accessAdmin.isRoleMember(address(accessAdmin), adminRole), "access admin contract does not have admin role"); + assertFalse(accessAdmin.isRoleMember(accessAdminDeployer, adminRole), "access admin deployer does have admin role"); // some more checks on access admin _checkAccessAdmin(accessAdmin, accessAdminDeployer); @@ -683,7 +680,7 @@ contract AccessAdminTest is AccessAdminBaseTest { vm.stopPrank(); // THEN - assertTrue(accessAdmin.hasRole(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); + assertTrue(accessAdmin.isRoleMember(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); _checkRole( accessAdmin, @@ -737,7 +734,7 @@ contract AccessAdminTest is AccessAdminBaseTest { roleNameBase); // THEN - assertTrue(accessAdmin.hasRole(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); + assertTrue(accessAdmin.isRoleMember(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); _checkRole( accessAdmin, @@ -770,93 +767,6 @@ contract AccessAdminTest is AccessAdminBaseTest { account2); } - function test_accessAdminCreateRole2LevelAddingMissingAdminRole() public { - // GIVEN - - RoleId adminRoleId = accessAdmin.getManagerRole(); - uint64 roleIdInt = 42; - string memory roleNameBase = "test"; - address account1 = makeAddr("account1"); - address account2 = makeAddr("account2"); - - ( - RoleId newRoleId, - RoleId newAdminRoleId, - string memory newRoleName, - string memory newAdminRoleName, - address roleAdmin - ) = _createManagedRoleWithOwnAdmin( - roleIdInt, - roleNameBase); - - assertFalse(accessAdmin.hasRole(account1, newRoleId), "account1 already has new role"); - assertTrue(accessAdmin.hasRole(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); - assertFalse(accessAdmin.hasRole(accessAdminDeployer, newAdminRoleId), "accessAdminDeployer having new role admin role"); - - // WHEN + THEN - vm.expectRevert( - abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminNotAdminOfRole.selector, - newAdminRoleId, - accessAdminDeployer)); - - vm.startPrank(accessAdminDeployer); - accessAdmin.grantRole(account1, newRoleId); - vm.stopPrank(); - - // WHEN - vm.startPrank(accessAdminDeployer); - accessAdmin.grantRole(accessAdminDeployer, newAdminRoleId); - accessAdmin.grantRole(account1, newRoleId); - vm.stopPrank(); - - // THEN - assertTrue(accessAdmin.hasRole(accessAdminDeployer, newAdminRoleId), "accessAdminDeployer missing new role admin role"); - assertTrue(accessAdmin.hasRole(account1, newRoleId), "account1 missing new role"); - } - - - function test_accessAdminCreateRole2LevelRemovingAdminRole() public { - // GIVEN - - RoleId adminRoleId = accessAdmin.getManagerRole(); - uint64 roleIdInt = 42; - string memory roleNameBase = "test"; - address account1 = makeAddr("account1"); - address account2 = makeAddr("account2"); - - ( - RoleId newRoleId, - RoleId newAdminRoleId, - string memory newRoleName, - string memory newAdminRoleName, - address roleAdmin - ) = _createManagedRoleWithOwnAdmin( - roleIdInt, - roleNameBase); - - assertFalse(accessAdmin.hasRole(account1, newRoleId), "account1 already has new role"); - assertTrue(accessAdmin.hasRole(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); - - // WHEN - vm.startPrank(accessAdminDeployer); - accessAdmin.revokeRole(roleAdmin, newAdminRoleId); - vm.stopPrank(); - - // THEN - assertFalse(accessAdmin.hasRole(roleAdmin, newAdminRoleId), "roleAdmin having new role admin role"); - - vm.expectRevert( - abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminNotAdminOfRole.selector, - newAdminRoleId, - roleAdmin)); - - vm.startPrank(roleAdmin); - accessAdmin.grantRole(account1, newRoleId); - vm.stopPrank(); - } - function test_accessAdminCreateRole2LevelGrantAndRevokeToMultipleAccounts() public { // GIVEN @@ -914,94 +824,6 @@ contract AccessAdminTest is AccessAdminBaseTest { } - function test_accessAdminCreateRole2LevelGrantRevokeRoleMultipleTimes() public { - // GIVEN - - RoleId adminRoleId = accessAdmin.getManagerRole(); - uint64 roleIdInt = 42; - string memory roleNameBase = "test"; - address account1 = makeAddr("account1"); - address account2 = makeAddr("account2"); - - ( - RoleId newRoleId, - RoleId newAdminRoleId, - string memory newRoleName, - string memory newAdminRoleName, - address roleAdmin - ) = _createManagedRoleWithOwnAdmin( - roleIdInt, - roleNameBase); - - assertFalse(accessAdmin.hasRole(account1, newRoleId), "account1 already has new role"); - assertTrue(accessAdmin.hasRole(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); - - // WHEN + THEN add multiple times - vm.startPrank(roleAdmin); - assertEq(accessAdmin.roleMembers(newRoleId), 0); - - accessAdmin.grantRole(account1, newRoleId); - assertEq(accessAdmin.roleMembers(newRoleId), 1); - - accessAdmin.grantRole(account2, newRoleId); - assertEq(accessAdmin.roleMembers(newRoleId), 2); - - assertTrue(accessAdmin.hasRole(account1, newRoleId), "account1 doesn't have new role"); - - // grant new role 2nd time to account 1 - accessAdmin.grantRole(account1, newRoleId); - assertEq(accessAdmin.roleMembers(newRoleId), 2); - assertTrue(accessAdmin.hasRole(account1, newRoleId), "account1 doesn't have new role"); - - accessAdmin.revokeRole(account1, newRoleId); - assertEq(accessAdmin.roleMembers(newRoleId), 1); - assertFalse(accessAdmin.hasRole(account1, newRoleId), "account1 still has new role"); - - // revoke new role 2nd time from account 1 - accessAdmin.revokeRole(account1, newRoleId); - - assertEq(accessAdmin.roleMembers(newRoleId), 1); - assertFalse(accessAdmin.hasRole(account1, newRoleId), "account1 still has new role"); - vm.stopPrank(); - - vm.startPrank(account1); - - // attempt to renounce role account1 no longer has - vm.expectRevert( - abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminNotRoleOwner.selector, - newRoleId, - account1)); - - accessAdmin.renounceRole(newRoleId); - - assertEq(accessAdmin.roleMembers(newRoleId), 1); - assertFalse(accessAdmin.hasRole(account1, newRoleId), "account1 still has new role"); - vm.stopPrank(); - - vm.startPrank(account2); - assertTrue(accessAdmin.hasRole(account2, newRoleId), "account2 doesn't have new role"); - - // renouncing 1st time has effect - accessAdmin.renounceRole(newRoleId); - assertEq(accessAdmin.roleMembers(newRoleId), 0); - assertFalse(accessAdmin.hasRole(account2, newRoleId), "account2 still has new role"); - - // remove role account2 no longer has - vm.expectRevert( - abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminNotRoleOwner.selector, - newRoleId, - account2)); - - accessAdmin.renounceRole(newRoleId); - - assertEq(accessAdmin.roleMembers(newRoleId), 0); - assertFalse(accessAdmin.hasRole(account2, newRoleId), "account2 still has new role"); - vm.stopPrank(); - } - - function test_accessAdminCreateRoleUnauthorized() public { // GIVEN (just setup) RoleId newRoleId = RoleIdLib.toRoleId(100); @@ -1186,9 +1008,9 @@ contract AccessAdminTest is AccessAdminBaseTest { { // GIVEN RoleId adminRoleId = aa.getRoleInfo(roleId).adminRoleId; - assertFalse(aa.hasRole(account1, roleId), "account1 already has role"); - assertFalse(aa.hasRole(account1, adminRoleId), "account1 is role admin"); - assertTrue(aa.hasRole(roleAdmin, adminRoleId), "role admin is not role admin"); + assertFalse(aa.isRoleMember(account1, roleId), "account1 already has role"); + assertFalse(aa.isRoleMember(account1, adminRoleId), "account1 is role admin"); + assertTrue(aa.isRoleMember(roleAdmin, adminRoleId), "role admin is not role admin"); // WHEN - grant role vm.startPrank(roleAdmin); @@ -1197,10 +1019,10 @@ contract AccessAdminTest is AccessAdminBaseTest { vm.stopPrank(); // THEN (grant) - assertTrue(aa.hasRole(account1, roleId), "outsider has not been granted role"); - assertTrue(aa.hasRole(account2, roleId), "outsider2 has not been granted role"); - assertFalse(aa.hasRole(account1, adminRoleId), "outsider is role admin"); - assertFalse(aa.hasRole(account2, adminRoleId), "outsider2 is role admin"); + assertTrue(aa.isRoleMember(account1, roleId), "outsider has not been granted role"); + assertTrue(aa.isRoleMember(account2, roleId), "outsider2 has not been granted role"); + assertFalse(aa.isRoleMember(account1, adminRoleId), "outsider is role admin"); + assertFalse(aa.isRoleMember(account2, adminRoleId), "outsider2 is role admin"); // WHEN - revoke role vm.startPrank(roleAdmin); @@ -1208,8 +1030,8 @@ contract AccessAdminTest is AccessAdminBaseTest { vm.stopPrank(); // THEN (revoke) - assertFalse(aa.hasRole(account1, roleId), "outsider still has role"); - assertFalse(aa.hasRole(account1, adminRoleId), "outsider is role admin"); + assertFalse(aa.isRoleMember(account1, roleId), "outsider still has role"); + assertFalse(aa.isRoleMember(account1, adminRoleId), "outsider is role admin"); // WHEN - renounce role vm.startPrank(account2); @@ -1217,8 +1039,8 @@ contract AccessAdminTest is AccessAdminBaseTest { vm.stopPrank(); // THEN (renounce) - assertFalse(aa.hasRole(account2, roleId), "outsider2 still has role"); - assertFalse(aa.hasRole(account2, adminRoleId), "outsider2 is role admin"); + assertFalse(aa.isRoleMember(account2, roleId), "outsider2 still has role"); + assertFalse(aa.isRoleMember(account2, adminRoleId), "outsider2 is role admin"); } @@ -1234,19 +1056,19 @@ contract AccessAdminTest is AccessAdminBaseTest { assertEq(aa.deployer(), expectedDeployer, "unexpected deployer"); // check aa roles - assertTrue(aa.hasRole(address(aa), aa.getAdminRole()), "access admin missing admin role"); - assertFalse(aa.hasRole(address(aa), aa.getManagerRole()), "access admin has manager role"); - assertTrue(aa.hasRole(address(aa), aa.getPublicRole()), "access admin missing public role"); + assertTrue(aa.isRoleMember(address(aa), aa.getAdminRole()), "access admin missing admin role"); + assertFalse(aa.isRoleMember(address(aa), aa.getManagerRole()), "access admin has manager role"); + assertTrue(aa.isRoleMember(address(aa), aa.getPublicRole()), "access admin missing public role"); // check deployer roles - assertFalse(aa.hasRole(expectedDeployer, aa.getAdminRole()), "deployer has admin role"); - assertTrue(aa.hasRole(expectedDeployer, aa.getManagerRole()), "deployer missing manager role"); - assertTrue(aa.hasRole(expectedDeployer, aa.getPublicRole()), "deployer missing public role"); + assertFalse(aa.isRoleMember(expectedDeployer, aa.getAdminRole()), "deployer has admin role"); + assertTrue(aa.isRoleMember(expectedDeployer, aa.getManagerRole()), "deployer missing manager role"); + assertTrue(aa.isRoleMember(expectedDeployer, aa.getPublicRole()), "deployer missing public role"); // check outsider roles - assertFalse(aa.hasRole(outsider, aa.getAdminRole()), "outsider has admin role"); - assertFalse(aa.hasRole(outsider, aa.getManagerRole()), "outsider has manager role"); - assertTrue(aa.hasRole(outsider, aa.getPublicRole()), "outsider missing public role"); + assertFalse(aa.isRoleMember(outsider, aa.getAdminRole()), "outsider has admin role"); + assertFalse(aa.isRoleMember(outsider, aa.getManagerRole()), "outsider has manager role"); + assertTrue(aa.isRoleMember(outsider, aa.getPublicRole()), "outsider missing public role"); // count roles and check role ids assertEq(aa.roles(), 3, "unexpected number of roles for freshly initialized access admin"); diff --git a/test/authorization/AccessAdminManageMock.t.sol b/test/authorization/AccessAdminManageMock.t.sol index ea5b9a250..de51113a5 100644 --- a/test/authorization/AccessAdminManageMock.t.sol +++ b/test/authorization/AccessAdminManageMock.t.sol @@ -73,8 +73,8 @@ contract AccessAdminManageMockTest is AccessAdminBaseTest { // WHEN (empty) // THEN RoleId adminRole = accessAdmin.getAdminRole(); - assertTrue(accessAdmin.hasRole(address(accessAdmin), adminRole), "access admin contract does not have admin role"); - assertFalse(accessAdmin.hasRole(accessAdminDeployer, adminRole), "access admin deployer does have admin role"); + assertTrue(accessAdmin.isRoleMember(address(accessAdmin), adminRole), "access admin contract does not have admin role"); + assertFalse(accessAdmin.isRoleMember(accessAdminDeployer, adminRole), "access admin deployer does have admin role"); assertTrue(accessAdmin.targetExists(target)); assertEq(accessAdmin.authorizedFunctions(target), 0, "unexpected initial number of authorized functions for target"); @@ -192,7 +192,7 @@ contract AccessAdminManageMockTest is AccessAdminBaseTest { // access admin is only address that has admin Role address accessAdminAddress = address(accessAdmin); assertEq(accessAdmin.roleMembers(adminRole), 1, "unexpected number of members for admin role"); - assertTrue(accessAdmin.hasRole(accessAdminAddress, adminRole), "access admin contract does not have admin role"); + assertTrue(accessAdmin.isRoleMember(accessAdminAddress, adminRole), "access admin contract does not have admin role"); // print initial target setup _printTarget(accessAdmin, target); @@ -312,19 +312,19 @@ contract AccessAdminManageMockTest is AccessAdminBaseTest { assertEq(aa.deployer(), expectedDeployer, "unexpected deployer"); // check aa roles - assertTrue(aa.hasRole(address(aa), aa.getAdminRole()), "access admin missing admin role"); - assertFalse(aa.hasRole(address(aa), aa.getManagerRole()), "access admin has manager role"); - assertTrue(aa.hasRole(address(aa), aa.getPublicRole()), "access admin missing public role"); + assertTrue(aa.isRoleMember(address(aa), aa.getAdminRole()), "access admin missing admin role"); + assertFalse(aa.isRoleMember(address(aa), aa.getManagerRole()), "access admin has manager role"); + assertTrue(aa.isRoleMember(address(aa), aa.getPublicRole()), "access admin missing public role"); // check deployer roles - assertFalse(aa.hasRole(expectedDeployer, aa.getAdminRole()), "deployer has admin role"); - assertTrue(aa.hasRole(expectedDeployer, aa.getManagerRole()), "deployer missing manager role"); - assertTrue(aa.hasRole(expectedDeployer, aa.getPublicRole()), "deployer missing public role"); + assertFalse(aa.isRoleMember(expectedDeployer, aa.getAdminRole()), "deployer has admin role"); + assertTrue(aa.isRoleMember(expectedDeployer, aa.getManagerRole()), "deployer missing manager role"); + assertTrue(aa.isRoleMember(expectedDeployer, aa.getPublicRole()), "deployer missing public role"); // check outsider roles - assertFalse(aa.hasRole(outsider, aa.getAdminRole()), "outsider has admin role"); - assertFalse(aa.hasRole(outsider, aa.getManagerRole()), "outsider has manager role"); - assertTrue(aa.hasRole(outsider, aa.getPublicRole()), "outsider missing public role"); + assertFalse(aa.isRoleMember(outsider, aa.getAdminRole()), "outsider has admin role"); + assertFalse(aa.isRoleMember(outsider, aa.getManagerRole()), "outsider has manager role"); + assertTrue(aa.isRoleMember(outsider, aa.getPublicRole()), "outsider missing public role"); // count roles and check role ids assertEq(aa.roles(), 3, "unexpected number of roles for freshly initialized access admin"); diff --git a/test/authorization/RegistryAdminEx.sol b/test/authorization/RegistryAdminEx.sol index 482765f68..764feb48a 100644 --- a/test/authorization/RegistryAdminEx.sol +++ b/test/authorization/RegistryAdminEx.sol @@ -38,6 +38,6 @@ contract RegistryAdminEx is RegistryAdmin { FunctionInfo[] memory functions; functions = new FunctionInfo[](1); functions[0] = AccessAdminLib.toFunction(AccessManagedMock.increaseCounter1.selector, "increaseCounter1"); - _authorizeTargetFunctions(address(accessManagedMock), PUBLIC_ROLE(), functions); + _authorizeTargetFunctions(address(accessManagedMock), PUBLIC_ROLE(), functions, true); } } \ No newline at end of file diff --git a/test/instance/authorization/InstanceAuthz.t.sol b/test/instance/authorization/InstanceAuthz.t.sol index 4af99acd7..47f5252a3 100644 --- a/test/instance/authorization/InstanceAuthz.t.sol +++ b/test/instance/authorization/InstanceAuthz.t.sol @@ -5,6 +5,8 @@ import {console} from "../../../lib/forge-std/src/Test.sol"; import {IAccess} from "../../../contracts/authorization/IAccess.sol"; import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; +import {IInstance} from "../../../contracts/instance/IInstance.sol"; +import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; import {ComponentService} from "../../../contracts/shared/ComponentService.sol"; import {GifTest} from "../../base/GifTest.sol"; @@ -26,22 +28,26 @@ contract InstanceAuthorizationTest is GifTest { assertEq(instanceReader.roles(), 15, "unexpected initial instance roles count (reader)"); } - function test_instanceAuthorizationCreateRoleHappyCase() public { + + function test_instanceAuthorizationRoleCreateHappyCase() public { // GIVEN setup uint256 rolesBefore = instanceReader.roles(); string memory roleName = "MyCustomRole"; - RoleId adminRole = instanceReader.getInstanceOwnerRole(); uint32 maxMemberCount = 42; // WHEN - vm.prank(instanceOwner); + vm.startPrank(instanceOwner); RoleId myCustomRoleId = instance.createRole( roleName, - adminRole, + INSTANCE_OWNER_ROLE(), maxMemberCount); + vm.stopPrank(); // THEN assertTrue(myCustomRoleId.gtz(), "role id zero"); + assertTrue(instanceReader.roleExists(myCustomRoleId), "role not existing"); + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertTrue(instanceReader.isRoleCustom(myCustomRoleId), "role not custom"); assertEq(instanceReader.roles(), rolesBefore + 1, "unexpected roles count after createRole"); IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(myCustomRoleId); @@ -54,7 +60,64 @@ contract InstanceAuthorizationTest is GifTest { } - function test_instanceAuthorizationCreateRoleRoleNameEmpty() public { + function test_instanceAuthorizationRoleCreateStackedHappyCase() public { + // GIVEN setup + uint256 rolesBefore = instanceReader.roles(); + + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + + // WHEN create another role + string memory roleName = "MySackedRole"; + uint32 maxMemberCount = 100; + + vm.startPrank(instanceOwner); + RoleId myStackedRoleId = instance.createRole( + roleName, + myCustomRoleId, // admin of stacked role is the custom role + maxMemberCount); + vm.stopPrank(); + + // THEN + assertTrue(myStackedRoleId.gtz(), "role id zero"); + assertTrue(instanceReader.roleExists(myStackedRoleId), "role not existing"); + assertTrue(instanceReader.isRoleActive(myStackedRoleId), "role not active"); + assertTrue(instanceReader.isRoleCustom(myStackedRoleId), "role not custom"); + assertEq(instanceReader.roles(), rolesBefore + 2, "unexpected roles count after createRole"); + + IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(myStackedRoleId); + assertEq(roleInfo.adminRoleId.toInt(), myCustomRoleId.toInt(), "custom role not role admin"); + assertTrue(roleInfo.roleType == IAccess.RoleType.Custom, "not custom role type"); + assertEq(roleInfo.maxMemberCount, maxMemberCount, "unexpected max member count"); + assertEq(roleInfo.name.toString(), "MySackedRole", "unexpected role name"); + assertEq(roleInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected created at timestamp"); + assertEq(roleInfo.pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); + } + + + function test_instanceAuthorizationRoleCreateNotInstanceOwner() public { + // GIVEN setup + address someAccount = makeAddr("someAccount"); + string memory roleName = ""; + RoleId adminRole = instanceReader.getInstanceOwnerRole(); + uint32 maxMemberCount = 42; + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + INftOwnable.ErrorNftOwnableNotOwner.selector, + someAccount)); + + vm.prank(someAccount); + RoleId myCustomRoleId = instance.createRole( + roleName, + adminRole, + maxMemberCount); + + assertTrue(myCustomRoleId.eqz(), "role id not zero"); + } + + + function test_instanceAuthorizationRoleCreateRoleNameEmpty() public { // GIVEN setup string memory roleName = ""; RoleId adminRole = instanceReader.getInstanceOwnerRole(); @@ -76,12 +139,14 @@ contract InstanceAuthorizationTest is GifTest { } - function test_instanceAuthorizationCreateRoleAdminRoleNonexistent() public { + function test_instanceAuthorizationRoleCreateAdminRoleNonexistent() public { // GIVEN setup string memory roleName = "myCustomRole"; RoleId adminRole = RoleIdLib.toRoleId(12345); uint32 maxMemberCount = 42; + assertFalse(instanceReader.roleExists(adminRole), "admin role unexpectedly existing"); + // WHEN + THEN vm.expectRevert( abi.encodeWithSelector( @@ -98,6 +163,114 @@ contract InstanceAuthorizationTest is GifTest { } + function test_instanceAuthorizationRoleSetActiveHappyCase1() public { + // GIVEN setup + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + + // WHEN - dactivate + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, false); + + // THEN + assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); + + // WHEN - activate + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, true); + + // THEN + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); + } + + + function test_instanceAuthorizationRoleSetActiveHappyCase2() public { + // GIVEN setup + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + + // WHEN - activate active role + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, true); + + // THEN + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); + + // WHEN - deactivate active role + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, false); + + // THEN + assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); + + // WHEN - deactivate pused role + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, false); + + // THEN + assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); + } + + + function test_instanceAuthorizationRoleSetActiveRoleNonexistent() public { + // GIVEN setup + RoleId fakeRoleId = RoleIdLib.toRoleId(12345); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotCustomRole.selector, + fakeRoleId)); + + vm.prank(instanceOwner); + instance.setRoleActive(fakeRoleId, false); + } + + + function test_instanceAuthorizationRoleSetActiveRoleNotCustom() public { + // GIVEN setup + RoleId instanceRoleId = instance.getInstanceAdmin().getRoleForName("InstanceRole"); + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotCustomRole.selector, + instanceRoleId)); + + vm.prank(instanceOwner); + instance.setRoleActive(instanceRoleId, false); + } + + + function test_instanceAuthorizationRoleSetActiveNotInstanceOwner() public { + // GIVEN setup + address someAccount = makeAddr("someAccount"); + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotRoleAdmin.selector, + myCustomRoleId, + someAccount)); + + vm.prank(someAccount); + instance.setRoleActive(myCustomRoleId, false); + } + + + function _createRole(string memory roleName, RoleId adminRole, uint32 maxMemberCount) internal returns (RoleId) { + vm.prank(instanceOwner); + return instance.createRole(roleName, adminRole, maxMemberCount); + } + + function _printRoles() internal { // print roles for(uint256 i = 0; i < instanceReader.roles(); i++) { diff --git a/test/release/ReleaseRegistryConcrete.t.sol b/test/release/ReleaseRegistryConcrete.t.sol index 3e3cc15ee..58e75a51c 100644 --- a/test/release/ReleaseRegistryConcrete.t.sol +++ b/test/release/ReleaseRegistryConcrete.t.sol @@ -662,7 +662,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { assertEq(serviceNftId.toInt(), expectedNftId, "registerService() return unexpected value"); ReleaseAdmin releaseAdmin = ReleaseAdmin(nextReleaseInfo.releaseAdmin); assertTrue(AccessManagerCloneable(releaseAdmin.authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(releaseAdmin.hasRole(address(service), expectedServiceRoleId), "hasRole() return unexpected value"); + assertTrue(releaseAdmin.isRoleMember(address(service), expectedServiceRoleId), "isRoleMember() return unexpected value"); assertTrue(releaseAdmin.targetExists(address(service)), "targetExists() return unexpected value"); assertTrue(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #2"); assertFalse(registryAdmin.targetExists(address(service)), "targetExists() return unexpected value"); @@ -701,7 +701,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { ReleaseAdmin( nextReleaseInfo.releaseAdmin).authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(registryAdmin.hasRole(address(service), expectedServiceRoleIdForAllVersions), "hasRole() return unexpected value"); + assertTrue(registryAdmin.isRoleMember(address(service), expectedServiceRoleIdForAllVersions), "isRoleMember() return unexpected value"); _assert_releaseRegistry_getters(nextVersion, nextReleaseInfo); _assert_releaseRegistry_getters(prevVersion, prevReleaseInfo); @@ -823,7 +823,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { ReleaseAdmin releaseAdmin = ReleaseAdmin(nextReleaseInfo.releaseAdmin); assertTrue(AccessManagerCloneable(releaseAdmin.authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(releaseAdmin.hasRole(address(service), expectedServiceRoleId), "hasRole() return unexpected value"); + assertTrue(releaseAdmin.isRoleMember(address(service), expectedServiceRoleId), "isRoleMember() return unexpected value"); assertTrue(releaseAdmin.targetExists(address(service)), "targetExists() return unexpected value"); assertTrue(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #2"); assertFalse(registryAdmin.targetExists(address(service)), "targetExists() return unexpected value"); @@ -854,7 +854,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { // check activation assertFalse(AccessManagerCloneable(ReleaseAdmin(nextReleaseInfo.releaseAdmin) .authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(registryAdmin.hasRole(address(service), expectedServiceRoleIdForAllVersions), "hasRole() return unexpected value"); + assertTrue(registryAdmin.isRoleMember(address(service), expectedServiceRoleIdForAllVersions), "isRoleMember() return unexpected value"); _assert_releaseRegistry_getters(nextVersion, nextReleaseInfo); _assert_releaseRegistry_getters(prevVersion, prevReleaseInfo); @@ -989,7 +989,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { assertTrue(AccessManagerCloneable(ReleaseAdmin(nextReleaseInfo.releaseAdmin) .authority()).isLocked(), "isLocked() return unexpected value"); assertTrue(ReleaseAdmin(nextReleaseInfo.releaseAdmin) - .hasRole(address(service), expectedServiceRoleId), "hasRole() return unexpected value"); + .isRoleMember(address(service), expectedServiceRoleId), "isRoleMember() return unexpected value"); assertTrue(ReleaseAdmin(nextReleaseInfo.releaseAdmin) .targetExists(address(service)), "targetExists() return unexpected value"); assertTrue(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #2"); @@ -1021,7 +1021,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { // check activation assertFalse(AccessManagerCloneable(ReleaseAdmin(nextReleaseInfo.releaseAdmin) .authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(registryAdmin.hasRole(address(service), expectedServiceRoleIdForAllVersions), "hasRole() return unexpected value"); + assertTrue(registryAdmin.isRoleMember(address(service), expectedServiceRoleIdForAllVersions), "isRoleMember() return unexpected value"); _assert_releaseRegistry_getters(nextVersion, nextReleaseInfo); _assert_releaseRegistry_getters(prevVersion, prevReleaseInfo); From 9292db01ea4b7da90d6b1f94d705d671836790ab Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Wed, 28 Aug 2024 14:57:34 +0000 Subject: [PATCH 04/18] add role granting --- contracts/authorization/AccessAdmin.sol | 13 +- contracts/authorization/Authorization.sol | 5 + contracts/authorization/IAccessAdmin.sol | 5 +- contracts/instance/IInstance.sol | 6 + contracts/instance/Instance.sol | 18 +- contracts/instance/InstanceReader.sol | 15 +- test/GifDeployer.t.sol | 4 +- test/authorization/AccessAdmin.t.sol | 48 +- .../authorization/AccessAdminManageMock.t.sol | 24 +- .../authorization/InstanceAuthz.t.sol | 282 --------- .../authorization/InstanceAuthzRoles.t.sol | 574 ++++++++++++++++++ test/release/ReleaseRegistryConcrete.t.sol | 12 +- 12 files changed, 659 insertions(+), 347 deletions(-) delete mode 100644 test/instance/authorization/InstanceAuthz.t.sol create mode 100644 test/instance/authorization/InstanceAuthzRoles.t.sol diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index 532d240fd..2b144ecfd 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -15,7 +15,7 @@ import {ContractLib} from "../shared/ContractLib.sol"; import {NftId, NftIdLib} from "../type/NftId.sol"; import {ObjectType} from "../type/ObjectType.sol"; import {RoleId, RoleIdLib, ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; -import {Selector, SelectorLib, SelectorSetLib} from "../type/Selector.sol"; +import {Selector, SelectorSetLib} from "../type/Selector.sol"; import {Str, StrLib} from "../type/String.sol"; import {TimestampLib} from "../type/Timestamp.sol"; import {VersionPart} from "../type/Version.sol"; @@ -254,21 +254,20 @@ contract AccessAdmin is return _roleMembers[roleId].at(idx); } - // TODO false because not role member or because role not exists? - function isRoleMember(address account, RoleId roleId) public view returns (bool) { + function isRoleMember(RoleId roleId, address account) public view returns (bool) { (bool isMember, ) = _authority.hasRole( RoleId.unwrap(roleId), account); return isMember; } - function isRoleAdmin(address account, RoleId roleId) + function isRoleAdmin(RoleId roleId, address account) public virtual view returns (bool) { - return isRoleMember(account, _roleInfo[roleId].adminRoleId); + return isRoleMember(_roleInfo[roleId].adminRoleId, account); } //--- view functions for targets ----------------------------------------// @@ -514,7 +513,7 @@ contract AccessAdmin is revert ErrorAccessAdminRoleMemberNotContract(roleId, account); } - // TODO check account already have roleId + // effects _roleMembers[roleId].add(account); _authority.grantRole( RoleId.unwrap(roleId), @@ -535,7 +534,7 @@ contract AccessAdmin is revert ErrorAccessAdminRoleMemberRemovalDisabled(roleId, account); } - // TODO check account have roleId? + // effects _roleMembers[roleId].remove(account); _authority.revokeRole( RoleId.unwrap(roleId), diff --git a/contracts/authorization/Authorization.sol b/contracts/authorization/Authorization.sol index 07d7c337e..e12467d3e 100644 --- a/contracts/authorization/Authorization.sol +++ b/contracts/authorization/Authorization.sol @@ -179,22 +179,27 @@ contract Authorization is /// @dev Sets up the relevant service targets for the component. /// Overwrite this function for use case specific authorizations. + // solhint-disable-next-line no-empty-blocks function _setupServiceTargets() internal virtual { } /// @dev Sets up the relevant (non-service) targets for the component. /// Overwrite this function for use case specific authorizations. + // solhint-disable-next-line no-empty-blocks function _setupTargets() internal virtual { } /// @dev Sets up the relevant roles for the component. /// Overwrite this function for use case specific authorizations. + // solhint-disable-next-line no-empty-blocks function _setupRoles() internal virtual {} /// @dev Sets up the relevant component's token handler authorizations. /// Overwrite this function for use case specific authorizations. + // solhint-disable-next-line no-empty-blocks function _setupTokenHandlerAuthorizations() internal virtual {} /// @dev Sets up the relevant target authorizations for the component. /// Overwrite this function for use case specific authorizations. + // solhint-disable-next-line no-empty-blocks function _setupTargetAuthorizations() internal virtual {} function _addGifContractTarget(string memory contractName) internal { diff --git a/contracts/authorization/IAccessAdmin.sol b/contracts/authorization/IAccessAdmin.sol index 0ea7fe586..be9adc2d0 100644 --- a/contracts/authorization/IAccessAdmin.sol +++ b/contracts/authorization/IAccessAdmin.sol @@ -11,7 +11,6 @@ import {IRelease} from "../registry/IRelease.sol"; import {NftId} from "../type/NftId.sol"; import {ObjectType} from "../type/ObjectType.sol"; import {RoleId} from "../type/RoleId.sol"; -import {Selector} from "../type/Selector.sol"; import {Str} from "../type/String.sol"; import {VersionPart} from "../type/Version.sol"; @@ -156,8 +155,8 @@ interface IAccessAdmin is function isRoleActive(RoleId roleId) external view returns (bool isActive); function isRoleCustom(RoleId roleId) external view returns (bool isCustom); - function isRoleMember(address account, RoleId roleId) external view returns (bool); - function isRoleAdmin(address account, RoleId roleId) external view returns (bool); + function isRoleMember(RoleId roleId, address account) external view returns (bool); + function isRoleAdmin(RoleId roleId, address account) external view returns (bool); function roleMembers(RoleId roleId) external view returns (uint256 numberOfMembers); function getRoleMember(RoleId roleId, uint256 idx) external view returns (address account); diff --git a/contracts/instance/IInstance.sol b/contracts/instance/IInstance.sol index 02f2aa3da..2e94c3653 100644 --- a/contracts/instance/IInstance.sol +++ b/contracts/instance/IInstance.sol @@ -17,6 +17,12 @@ import {UFixed} from "../type/UFixed.sol"; interface IInstance is IRegisterable { + // role handling + event LogInstanceCustomRoleCreated(RoleId roleId, string roleName, RoleId adminRoleId, uint32 maxMemberCount); + event LogInstanceCustomRoleActiveSet(RoleId roleId, bool active, address caller); + event LogInstanceCustomRoleGranted(RoleId roleId, address account, address caller); + event LogInstanceCustomRoleRevoked(RoleId roleId, address account, address caller); + // modifier is onlyRoleAdmin error ErrorInstanceNotCustomRole(RoleId roleId); error ErrorInstanceNotRoleAdmin(RoleId roleId, address account); diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index 5e0e8b84f..62793e141 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -37,7 +37,7 @@ contract Instance is NftId [] internal _products; - modifier onlyRoleAdmin(RoleId roleId) { + modifier onlyCustomRoleAdmin(RoleId roleId) { if (!_instanceAdmin.isRoleCustom(roleId)) { revert ErrorInstanceNotCustomRole(roleId); } @@ -45,7 +45,7 @@ contract Instance is // instance owner can always act as role admin address account = msg.sender; if (account != getOwner()) { - if (!_instanceAdmin.isRoleAdmin(account, roleId)) { + if (!_instanceAdmin.isRoleAdmin(roleId, account)) { revert ErrorInstanceNotRoleAdmin(roleId, account); } } @@ -187,7 +187,8 @@ contract Instance is onlyOwner() returns (RoleId roleId) { - return _instanceService.createRole(roleName, adminRoleId, maxMemberCount); + roleId = _instanceService.createRole(roleName, adminRoleId, maxMemberCount); + emit LogInstanceCustomRoleCreated(roleId, roleName, adminRoleId, maxMemberCount); } @@ -195,9 +196,10 @@ contract Instance is function setRoleActive(RoleId roleId, bool active) external restricted() - onlyRoleAdmin(roleId) + onlyCustomRoleAdmin(roleId) { _instanceService.setRoleActive(roleId, active); + emit LogInstanceCustomRoleActiveSet(roleId, active, msg.sender); } @@ -205,9 +207,10 @@ contract Instance is function grantRole(RoleId roleId, address account) external restricted() - onlyRoleAdmin(roleId) + onlyCustomRoleAdmin(roleId) { _instanceService.grantRole(roleId, account); + emit LogInstanceCustomRoleGranted(roleId, account, msg.sender); } @@ -215,9 +218,10 @@ contract Instance is function revokeRole(RoleId roleId, address account) external restricted() - onlyRoleAdmin(roleId) + onlyCustomRoleAdmin(roleId) { - _instanceService.grantRole(roleId, account); + _instanceService.revokeRole(roleId, account); + emit LogInstanceCustomRoleRevoked(roleId, account, msg.sender); } //--- Targets ------------------------------------------------------------// diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index 782ef7376..c6fd169fc 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -596,14 +596,21 @@ contract InstanceReader { return _instance.getInstanceAdmin().isRoleActive(roleId); } + function roleMembers(RoleId roleId) public view returns (uint256 numberOfMembers) { + return _instance.getInstanceAdmin().roleMembers(roleId); + } + + function getRoleMember(RoleId roleId, uint256 idx) public view returns (address account) { + return _instance.getInstanceAdmin().getRoleMember(roleId, idx); + } - function isRoleMember(address account, RoleId roleId) public view returns (bool isMember) { - return _instance.getInstanceAdmin().isRoleMember(account, roleId); + function isRoleMember(RoleId roleId, address account) public view returns (bool isMember) { + return _instance.getInstanceAdmin().isRoleMember(roleId, account); } - function isRoleAdmin(address account, RoleId roleId) public view returns (bool isMember) { - return _instance.getInstanceAdmin().isRoleAdmin(account, roleId); + function isRoleAdmin(RoleId roleId, address account) public view returns (bool isMember) { + return _instance.getInstanceAdmin().isRoleAdmin(roleId, account); } diff --git a/test/GifDeployer.t.sol b/test/GifDeployer.t.sol index 9d28ceea7..0a5e999b5 100644 --- a/test/GifDeployer.t.sol +++ b/test/GifDeployer.t.sol @@ -115,8 +115,8 @@ contract GifDeployerTest is GifDeployer { assertEq(registryAdmin.authority(), registryAdmin.authority(), "unexpected release manager authority"); // check initial roles assignments - assertTrue(registryAdmin.isRoleMember(gifAdmin, GIF_ADMIN_ROLE()), "registry owner not admin"); - assertTrue(registryAdmin.isRoleMember(gifManager, GIF_MANAGER_ROLE()), "registry owner not manager"); + assertTrue(registryAdmin.isRoleMember(GIF_ADMIN_ROLE(), gifAdmin), "registry owner not admin"); + assertTrue(registryAdmin.isRoleMember(GIF_MANAGER_ROLE(), gifManager), "registry owner not manager"); // check sample admin access IAccessManager authority = IAccessManager(registryAdmin.authority()); diff --git a/test/authorization/AccessAdmin.t.sol b/test/authorization/AccessAdmin.t.sol index 0a1829d38..181cd357c 100644 --- a/test/authorization/AccessAdmin.t.sol +++ b/test/authorization/AccessAdmin.t.sol @@ -296,8 +296,8 @@ contract AccessAdminTest is AccessAdminBaseTest { // WHEN (empty) // THEN RoleId adminRole = accessAdmin.getAdminRole(); - assertTrue(accessAdmin.isRoleMember(address(accessAdmin), adminRole), "access admin contract does not have admin role"); - assertFalse(accessAdmin.isRoleMember(accessAdminDeployer, adminRole), "access admin deployer does have admin role"); + assertTrue(accessAdmin.isRoleMember(adminRole, address(accessAdmin)), "access admin contract does not have admin role"); + assertFalse(accessAdmin.isRoleMember(adminRole, accessAdminDeployer), "access admin deployer does have admin role"); // some more checks on access admin _checkAccessAdmin(accessAdmin, accessAdminDeployer); @@ -680,7 +680,7 @@ contract AccessAdminTest is AccessAdminBaseTest { vm.stopPrank(); // THEN - assertTrue(accessAdmin.isRoleMember(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); + assertTrue(accessAdmin.isRoleMember(newAdminRoleId, roleAdmin), "roleAdmin not having new role admin role"); _checkRole( accessAdmin, @@ -734,7 +734,7 @@ contract AccessAdminTest is AccessAdminBaseTest { roleNameBase); // THEN - assertTrue(accessAdmin.isRoleMember(roleAdmin, newAdminRoleId), "roleAdmin not having new role admin role"); + assertTrue(accessAdmin.isRoleMember(newAdminRoleId, roleAdmin), "roleAdmin not having new role admin role"); _checkRole( accessAdmin, @@ -1008,9 +1008,9 @@ contract AccessAdminTest is AccessAdminBaseTest { { // GIVEN RoleId adminRoleId = aa.getRoleInfo(roleId).adminRoleId; - assertFalse(aa.isRoleMember(account1, roleId), "account1 already has role"); - assertFalse(aa.isRoleMember(account1, adminRoleId), "account1 is role admin"); - assertTrue(aa.isRoleMember(roleAdmin, adminRoleId), "role admin is not role admin"); + assertFalse(aa.isRoleMember(roleId, account1), "account1 already has role"); + assertFalse(aa.isRoleMember(adminRoleId, account1), "account1 is role admin"); + assertTrue(aa.isRoleMember(adminRoleId, roleAdmin), "role admin is not role admin"); // WHEN - grant role vm.startPrank(roleAdmin); @@ -1019,10 +1019,10 @@ contract AccessAdminTest is AccessAdminBaseTest { vm.stopPrank(); // THEN (grant) - assertTrue(aa.isRoleMember(account1, roleId), "outsider has not been granted role"); - assertTrue(aa.isRoleMember(account2, roleId), "outsider2 has not been granted role"); - assertFalse(aa.isRoleMember(account1, adminRoleId), "outsider is role admin"); - assertFalse(aa.isRoleMember(account2, adminRoleId), "outsider2 is role admin"); + assertTrue(aa.isRoleMember(roleId, account1), "outsider has not been granted role"); + assertTrue(aa.isRoleMember(roleId, account2), "outsider2 has not been granted role"); + assertFalse(aa.isRoleMember(adminRoleId, account1), "outsider is role admin"); + assertFalse(aa.isRoleMember(adminRoleId, account2), "outsider2 is role admin"); // WHEN - revoke role vm.startPrank(roleAdmin); @@ -1030,8 +1030,8 @@ contract AccessAdminTest is AccessAdminBaseTest { vm.stopPrank(); // THEN (revoke) - assertFalse(aa.isRoleMember(account1, roleId), "outsider still has role"); - assertFalse(aa.isRoleMember(account1, adminRoleId), "outsider is role admin"); + assertFalse(aa.isRoleMember(roleId, account1), "outsider still has role"); + assertFalse(aa.isRoleMember(adminRoleId, account1), "outsider is role admin"); // WHEN - renounce role vm.startPrank(account2); @@ -1039,8 +1039,8 @@ contract AccessAdminTest is AccessAdminBaseTest { vm.stopPrank(); // THEN (renounce) - assertFalse(aa.isRoleMember(account2, roleId), "outsider2 still has role"); - assertFalse(aa.isRoleMember(account2, adminRoleId), "outsider2 is role admin"); + assertFalse(aa.isRoleMember(roleId, account2), "outsider2 still has role"); + assertFalse(aa.isRoleMember(adminRoleId, account2), "outsider2 is role admin"); } @@ -1056,19 +1056,19 @@ contract AccessAdminTest is AccessAdminBaseTest { assertEq(aa.deployer(), expectedDeployer, "unexpected deployer"); // check aa roles - assertTrue(aa.isRoleMember(address(aa), aa.getAdminRole()), "access admin missing admin role"); - assertFalse(aa.isRoleMember(address(aa), aa.getManagerRole()), "access admin has manager role"); - assertTrue(aa.isRoleMember(address(aa), aa.getPublicRole()), "access admin missing public role"); + assertTrue(aa.isRoleMember(aa.getAdminRole(), address(aa)), "access admin missing admin role"); + assertFalse(aa.isRoleMember(aa.getManagerRole(), address(aa)), "access admin has manager role"); + assertTrue(aa.isRoleMember(aa.getPublicRole(), address(aa)), "access admin missing public role"); // check deployer roles - assertFalse(aa.isRoleMember(expectedDeployer, aa.getAdminRole()), "deployer has admin role"); - assertTrue(aa.isRoleMember(expectedDeployer, aa.getManagerRole()), "deployer missing manager role"); - assertTrue(aa.isRoleMember(expectedDeployer, aa.getPublicRole()), "deployer missing public role"); + assertFalse(aa.isRoleMember(aa.getAdminRole(), expectedDeployer), "deployer has admin role"); + assertTrue(aa.isRoleMember(aa.getManagerRole(), expectedDeployer), "deployer missing manager role"); + assertTrue(aa.isRoleMember(aa.getPublicRole(), expectedDeployer), "deployer missing public role"); // check outsider roles - assertFalse(aa.isRoleMember(outsider, aa.getAdminRole()), "outsider has admin role"); - assertFalse(aa.isRoleMember(outsider, aa.getManagerRole()), "outsider has manager role"); - assertTrue(aa.isRoleMember(outsider, aa.getPublicRole()), "outsider missing public role"); + assertFalse(aa.isRoleMember(aa.getAdminRole(), outsider), "outsider has admin role"); + assertFalse(aa.isRoleMember(aa.getManagerRole(), outsider), "outsider has manager role"); + assertTrue(aa.isRoleMember(aa.getPublicRole(), outsider), "outsider missing public role"); // count roles and check role ids assertEq(aa.roles(), 3, "unexpected number of roles for freshly initialized access admin"); diff --git a/test/authorization/AccessAdminManageMock.t.sol b/test/authorization/AccessAdminManageMock.t.sol index de51113a5..cf205c164 100644 --- a/test/authorization/AccessAdminManageMock.t.sol +++ b/test/authorization/AccessAdminManageMock.t.sol @@ -73,8 +73,8 @@ contract AccessAdminManageMockTest is AccessAdminBaseTest { // WHEN (empty) // THEN RoleId adminRole = accessAdmin.getAdminRole(); - assertTrue(accessAdmin.isRoleMember(address(accessAdmin), adminRole), "access admin contract does not have admin role"); - assertFalse(accessAdmin.isRoleMember(accessAdminDeployer, adminRole), "access admin deployer does have admin role"); + assertTrue(accessAdmin.isRoleMember(adminRole, address(accessAdmin)), "access admin contract does not have admin role"); + assertFalse(accessAdmin.isRoleMember(adminRole, accessAdminDeployer), "access admin deployer does have admin role"); assertTrue(accessAdmin.targetExists(target)); assertEq(accessAdmin.authorizedFunctions(target), 0, "unexpected initial number of authorized functions for target"); @@ -192,7 +192,7 @@ contract AccessAdminManageMockTest is AccessAdminBaseTest { // access admin is only address that has admin Role address accessAdminAddress = address(accessAdmin); assertEq(accessAdmin.roleMembers(adminRole), 1, "unexpected number of members for admin role"); - assertTrue(accessAdmin.isRoleMember(accessAdminAddress, adminRole), "access admin contract does not have admin role"); + assertTrue(accessAdmin.isRoleMember(adminRole, accessAdminAddress), "access admin contract does not have admin role"); // print initial target setup _printTarget(accessAdmin, target); @@ -312,19 +312,19 @@ contract AccessAdminManageMockTest is AccessAdminBaseTest { assertEq(aa.deployer(), expectedDeployer, "unexpected deployer"); // check aa roles - assertTrue(aa.isRoleMember(address(aa), aa.getAdminRole()), "access admin missing admin role"); - assertFalse(aa.isRoleMember(address(aa), aa.getManagerRole()), "access admin has manager role"); - assertTrue(aa.isRoleMember(address(aa), aa.getPublicRole()), "access admin missing public role"); + assertTrue(aa.isRoleMember(aa.getAdminRole(), address(aa)), "access admin missing admin role"); + assertFalse(aa.isRoleMember(aa.getManagerRole(), address(aa)), "access admin has manager role"); + assertTrue(aa.isRoleMember(aa.getPublicRole(), address(aa)), "access admin missing public role"); // check deployer roles - assertFalse(aa.isRoleMember(expectedDeployer, aa.getAdminRole()), "deployer has admin role"); - assertTrue(aa.isRoleMember(expectedDeployer, aa.getManagerRole()), "deployer missing manager role"); - assertTrue(aa.isRoleMember(expectedDeployer, aa.getPublicRole()), "deployer missing public role"); + assertFalse(aa.isRoleMember(aa.getAdminRole(), expectedDeployer), "deployer has admin role"); + assertTrue(aa.isRoleMember(aa.getManagerRole(), expectedDeployer), "deployer missing manager role"); + assertTrue(aa.isRoleMember(aa.getPublicRole(), expectedDeployer), "deployer missing public role"); // check outsider roles - assertFalse(aa.isRoleMember(outsider, aa.getAdminRole()), "outsider has admin role"); - assertFalse(aa.isRoleMember(outsider, aa.getManagerRole()), "outsider has manager role"); - assertTrue(aa.isRoleMember(outsider, aa.getPublicRole()), "outsider missing public role"); + assertFalse(aa.isRoleMember(aa.getAdminRole(), outsider), "outsider has admin role"); + assertFalse(aa.isRoleMember(aa.getManagerRole(), outsider), "outsider has manager role"); + assertTrue(aa.isRoleMember(aa.getPublicRole(), outsider), "outsider missing public role"); // count roles and check role ids assertEq(aa.roles(), 3, "unexpected number of roles for freshly initialized access admin"); diff --git a/test/instance/authorization/InstanceAuthz.t.sol b/test/instance/authorization/InstanceAuthz.t.sol deleted file mode 100644 index 47f5252a3..000000000 --- a/test/instance/authorization/InstanceAuthz.t.sol +++ /dev/null @@ -1,282 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.20; - -import {console} from "../../../lib/forge-std/src/Test.sol"; - -import {IAccess} from "../../../contracts/authorization/IAccess.sol"; -import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; -import {IInstance} from "../../../contracts/instance/IInstance.sol"; -import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; - -import {ComponentService} from "../../../contracts/shared/ComponentService.sol"; -import {GifTest} from "../../base/GifTest.sol"; -import {FeeLib} from "../../../contracts/type/Fee.sol"; -import {NftId, NftIdLib} from "../../../contracts/type/NftId.sol"; -import {RoleId,RoleIdLib, ADMIN_ROLE, INSTANCE_OWNER_ROLE} from "../../../contracts/type/RoleId.sol"; -import {SimplePool} from "../../../contracts/examples/unpermissioned/SimplePool.sol"; -import {SimpleProduct} from "../../../contracts/examples/unpermissioned/SimpleProduct.sol"; -import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; -import {UFixedLib} from "../../../contracts/type/UFixed.sol"; - -contract InstanceAuthorizationTest is GifTest { - - function test_instanceAuthorizationSetup() public { - _printRoles(); - - // check initial roles - assertEq(instanceAdmin.roles(), 15, "unexpected initial instance roles count (admin)"); - assertEq(instanceReader.roles(), 15, "unexpected initial instance roles count (reader)"); - } - - - function test_instanceAuthorizationRoleCreateHappyCase() public { - // GIVEN setup - uint256 rolesBefore = instanceReader.roles(); - string memory roleName = "MyCustomRole"; - uint32 maxMemberCount = 42; - - // WHEN - vm.startPrank(instanceOwner); - RoleId myCustomRoleId = instance.createRole( - roleName, - INSTANCE_OWNER_ROLE(), - maxMemberCount); - vm.stopPrank(); - - // THEN - assertTrue(myCustomRoleId.gtz(), "role id zero"); - assertTrue(instanceReader.roleExists(myCustomRoleId), "role not existing"); - assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); - assertTrue(instanceReader.isRoleCustom(myCustomRoleId), "role not custom"); - assertEq(instanceReader.roles(), rolesBefore + 1, "unexpected roles count after createRole"); - - IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(myCustomRoleId); - assertEq(roleInfo.adminRoleId.toInt(), INSTANCE_OWNER_ROLE().toInt(), "instance owner role not role admin"); - assertTrue(roleInfo.roleType == IAccess.RoleType.Custom, "not custom role type"); - assertEq(roleInfo.maxMemberCount, maxMemberCount, "unexpected max member count"); - assertEq(roleInfo.name.toString(), "MyCustomRole", "unexpected role name"); - assertEq(roleInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected created at timestamp"); - assertEq(roleInfo.pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); - } - - - function test_instanceAuthorizationRoleCreateStackedHappyCase() public { - // GIVEN setup - uint256 rolesBefore = instanceReader.roles(); - - RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); - - // WHEN create another role - string memory roleName = "MySackedRole"; - uint32 maxMemberCount = 100; - - vm.startPrank(instanceOwner); - RoleId myStackedRoleId = instance.createRole( - roleName, - myCustomRoleId, // admin of stacked role is the custom role - maxMemberCount); - vm.stopPrank(); - - // THEN - assertTrue(myStackedRoleId.gtz(), "role id zero"); - assertTrue(instanceReader.roleExists(myStackedRoleId), "role not existing"); - assertTrue(instanceReader.isRoleActive(myStackedRoleId), "role not active"); - assertTrue(instanceReader.isRoleCustom(myStackedRoleId), "role not custom"); - assertEq(instanceReader.roles(), rolesBefore + 2, "unexpected roles count after createRole"); - - IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(myStackedRoleId); - assertEq(roleInfo.adminRoleId.toInt(), myCustomRoleId.toInt(), "custom role not role admin"); - assertTrue(roleInfo.roleType == IAccess.RoleType.Custom, "not custom role type"); - assertEq(roleInfo.maxMemberCount, maxMemberCount, "unexpected max member count"); - assertEq(roleInfo.name.toString(), "MySackedRole", "unexpected role name"); - assertEq(roleInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected created at timestamp"); - assertEq(roleInfo.pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); - } - - - function test_instanceAuthorizationRoleCreateNotInstanceOwner() public { - // GIVEN setup - address someAccount = makeAddr("someAccount"); - string memory roleName = ""; - RoleId adminRole = instanceReader.getInstanceOwnerRole(); - uint32 maxMemberCount = 42; - - // WHEN + THEN - vm.expectRevert( - abi.encodeWithSelector( - INftOwnable.ErrorNftOwnableNotOwner.selector, - someAccount)); - - vm.prank(someAccount); - RoleId myCustomRoleId = instance.createRole( - roleName, - adminRole, - maxMemberCount); - - assertTrue(myCustomRoleId.eqz(), "role id not zero"); - } - - - function test_instanceAuthorizationRoleCreateRoleNameEmpty() public { - // GIVEN setup - string memory roleName = ""; - RoleId adminRole = instanceReader.getInstanceOwnerRole(); - uint32 maxMemberCount = 42; - - // WHEN + THEN - vm.expectRevert( - abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminRoleNameEmpty.selector, - 1000000)); - - vm.prank(instanceOwner); - RoleId myCustomRoleId = instance.createRole( - roleName, - adminRole, - maxMemberCount); - - assertTrue(myCustomRoleId.eqz(), "role id not zero"); - } - - - function test_instanceAuthorizationRoleCreateAdminRoleNonexistent() public { - // GIVEN setup - string memory roleName = "myCustomRole"; - RoleId adminRole = RoleIdLib.toRoleId(12345); - uint32 maxMemberCount = 42; - - assertFalse(instanceReader.roleExists(adminRole), "admin role unexpectedly existing"); - - // WHEN + THEN - vm.expectRevert( - abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminRoleAdminNotExisting.selector, - adminRole)); - - vm.prank(instanceOwner); - RoleId myCustomRoleId = instance.createRole( - roleName, - adminRole, - maxMemberCount); - - assertTrue(myCustomRoleId.eqz(), "role id not zero"); - } - - - function test_instanceAuthorizationRoleSetActiveHappyCase1() public { - // GIVEN setup - RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); - - assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); - - // WHEN - dactivate - vm.prank(instanceOwner); - instance.setRoleActive(myCustomRoleId, false); - - // THEN - assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); - assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); - - // WHEN - activate - vm.prank(instanceOwner); - instance.setRoleActive(myCustomRoleId, true); - - // THEN - assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); - assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); - } - - - function test_instanceAuthorizationRoleSetActiveHappyCase2() public { - // GIVEN setup - RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); - - assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); - - // WHEN - activate active role - vm.prank(instanceOwner); - instance.setRoleActive(myCustomRoleId, true); - - // THEN - assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); - assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); - - // WHEN - deactivate active role - vm.prank(instanceOwner); - instance.setRoleActive(myCustomRoleId, false); - - // THEN - assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); - assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); - - // WHEN - deactivate pused role - vm.prank(instanceOwner); - instance.setRoleActive(myCustomRoleId, false); - - // THEN - assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); - assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); - } - - - function test_instanceAuthorizationRoleSetActiveRoleNonexistent() public { - // GIVEN setup - RoleId fakeRoleId = RoleIdLib.toRoleId(12345); - - // WHEN + THEN - vm.expectRevert( - abi.encodeWithSelector( - IInstance.ErrorInstanceNotCustomRole.selector, - fakeRoleId)); - - vm.prank(instanceOwner); - instance.setRoleActive(fakeRoleId, false); - } - - - function test_instanceAuthorizationRoleSetActiveRoleNotCustom() public { - // GIVEN setup - RoleId instanceRoleId = instance.getInstanceAdmin().getRoleForName("InstanceRole"); - // WHEN + THEN - vm.expectRevert( - abi.encodeWithSelector( - IInstance.ErrorInstanceNotCustomRole.selector, - instanceRoleId)); - - vm.prank(instanceOwner); - instance.setRoleActive(instanceRoleId, false); - } - - - function test_instanceAuthorizationRoleSetActiveNotInstanceOwner() public { - // GIVEN setup - address someAccount = makeAddr("someAccount"); - RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); - - // WHEN + THEN - vm.expectRevert( - abi.encodeWithSelector( - IInstance.ErrorInstanceNotRoleAdmin.selector, - myCustomRoleId, - someAccount)); - - vm.prank(someAccount); - instance.setRoleActive(myCustomRoleId, false); - } - - - function _createRole(string memory roleName, RoleId adminRole, uint32 maxMemberCount) internal returns (RoleId) { - vm.prank(instanceOwner); - return instance.createRole(roleName, adminRole, maxMemberCount); - } - - - function _printRoles() internal { - // print roles - for(uint256 i = 0; i < instanceReader.roles(); i++) { - RoleId roleId = instanceReader.getRoleId(i); - IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(roleId); - console.log("role", i, roleId.toInt(), roleInfo.name.toString()); - } - } -} \ No newline at end of file diff --git a/test/instance/authorization/InstanceAuthzRoles.t.sol b/test/instance/authorization/InstanceAuthzRoles.t.sol new file mode 100644 index 000000000..336577c0f --- /dev/null +++ b/test/instance/authorization/InstanceAuthzRoles.t.sol @@ -0,0 +1,574 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {console} from "../../../lib/forge-std/src/Test.sol"; + +import {IAccess} from "../../../contracts/authorization/IAccess.sol"; +import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; +import {IInstance} from "../../../contracts/instance/IInstance.sol"; +import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; + +import {ComponentService} from "../../../contracts/shared/ComponentService.sol"; +import {GifTest} from "../../base/GifTest.sol"; +import {FeeLib} from "../../../contracts/type/Fee.sol"; +import {NftId, NftIdLib} from "../../../contracts/type/NftId.sol"; +import {RoleId,RoleIdLib, ADMIN_ROLE, INSTANCE_OWNER_ROLE} from "../../../contracts/type/RoleId.sol"; +import {SimplePool} from "../../../contracts/examples/unpermissioned/SimplePool.sol"; +import {SimpleProduct} from "../../../contracts/examples/unpermissioned/SimpleProduct.sol"; +import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; +import {UFixedLib} from "../../../contracts/type/UFixed.sol"; + +contract InstanceAuthzRolesTest is GifTest { + + function test_instanceAuthzSetup() public { + _printRoles(); + + // check initial roles + assertEq(instanceAdmin.roles(), 15, "unexpected initial instance roles count (admin)"); + assertEq(instanceReader.roles(), 15, "unexpected initial instance roles count (reader)"); + } + + //--- role creation ----------------------------------------------------// + + function test_instanceAuthzRolesCreateHappyCase() public { + // GIVEN setup + uint256 rolesBefore = instanceReader.roles(); + string memory roleName = "MyCustomRole"; + RoleId expectedRoleId = RoleIdLib.toRoleId(1000000); + RoleId adminRoleId = INSTANCE_OWNER_ROLE(); + uint32 maxMemberCount = 42; + + // WHEN + vm.expectEmit(address(instance)); + emit IInstance.LogInstanceCustomRoleCreated( + expectedRoleId, + roleName, + adminRoleId, + maxMemberCount); + + vm.prank(instanceOwner); + RoleId myCustomRoleId = instance.createRole( + roleName, + adminRoleId, + maxMemberCount); + + // THEN + assertEq(myCustomRoleId.toInt(), expectedRoleId.toInt(), "unexpected role id"); + assertTrue(instanceReader.roleExists(myCustomRoleId), "role not existing"); + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertTrue(instanceReader.isRoleCustom(myCustomRoleId), "role not custom"); + assertEq(instanceReader.roles(), rolesBefore + 1, "unexpected roles count after createRole"); + + IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(myCustomRoleId); + assertEq(roleInfo.adminRoleId.toInt(), INSTANCE_OWNER_ROLE().toInt(), "instance owner role not role admin"); + assertTrue(roleInfo.roleType == IAccess.RoleType.Custom, "not custom role type"); + assertEq(roleInfo.maxMemberCount, maxMemberCount, "unexpected max member count"); + assertEq(roleInfo.name.toString(), "MyCustomRole", "unexpected role name"); + assertEq(roleInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected created at timestamp"); + assertEq(roleInfo.pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); + } + + + function test_instanceAuthzRolesCreateStackedHappyCase() public { + // GIVEN setup + uint256 rolesBefore = instanceReader.roles(); + + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + + // WHEN create another role + string memory roleName = "MySackedRole"; + uint32 maxMemberCount = 100; + + vm.startPrank(instanceOwner); + RoleId myStackedRoleId = instance.createRole( + roleName, + myCustomRoleId, // admin of stacked role is the custom role + maxMemberCount); + vm.stopPrank(); + + // THEN + assertTrue(myStackedRoleId.gtz(), "role id zero"); + assertTrue(instanceReader.roleExists(myStackedRoleId), "role not existing"); + assertTrue(instanceReader.isRoleActive(myStackedRoleId), "role not active"); + assertTrue(instanceReader.isRoleCustom(myStackedRoleId), "role not custom"); + assertEq(instanceReader.roles(), rolesBefore + 2, "unexpected roles count after createRole"); + + IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(myStackedRoleId); + assertEq(roleInfo.adminRoleId.toInt(), myCustomRoleId.toInt(), "custom role not role admin"); + assertTrue(roleInfo.roleType == IAccess.RoleType.Custom, "not custom role type"); + assertEq(roleInfo.maxMemberCount, maxMemberCount, "unexpected max member count"); + assertEq(roleInfo.name.toString(), "MySackedRole", "unexpected role name"); + assertEq(roleInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected created at timestamp"); + assertEq(roleInfo.pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); + } + + + function test_instanceAuthzRolesCreateNotInstanceOwner() public { + // GIVEN setup + address someAccount = makeAddr("someAccount"); + string memory roleName = ""; + RoleId adminRole = instanceReader.getInstanceOwnerRole(); + uint32 maxMemberCount = 42; + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + INftOwnable.ErrorNftOwnableNotOwner.selector, + someAccount)); + + vm.prank(someAccount); + RoleId myCustomRoleId = instance.createRole( + roleName, + adminRole, + maxMemberCount); + + assertTrue(myCustomRoleId.eqz(), "role id not zero"); + } + + + function test_instanceAuthzRolesCreateRoleNameEmpty() public { + // GIVEN setup + string memory roleName = ""; + RoleId adminRole = instanceReader.getInstanceOwnerRole(); + uint32 maxMemberCount = 42; + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IAccessAdmin.ErrorAccessAdminRoleNameEmpty.selector, + 1000000)); + + vm.prank(instanceOwner); + RoleId myCustomRoleId = instance.createRole( + roleName, + adminRole, + maxMemberCount); + + assertTrue(myCustomRoleId.eqz(), "role id not zero"); + } + + + function test_instanceAuthzRolesCreateAdminRoleNonexistent() public { + // GIVEN setup + string memory roleName = "myCustomRole"; + RoleId adminRole = RoleIdLib.toRoleId(12345); + uint32 maxMemberCount = 42; + + assertFalse(instanceReader.roleExists(adminRole), "admin role unexpectedly existing"); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IAccessAdmin.ErrorAccessAdminRoleAdminNotExisting.selector, + adminRole)); + + vm.prank(instanceOwner); + RoleId myCustomRoleId = instance.createRole( + roleName, + adminRole, + maxMemberCount); + + assertTrue(myCustomRoleId.eqz(), "role id not zero"); + } + + //--- role activation ----------------------------------------------------// + + function test_instanceAuthzRolesSetActiveHappyCase1() public { + // GIVEN setup + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + + // WHEN - dactivate + vm.expectEmit(address(instance)); + emit IInstance.LogInstanceCustomRoleActiveSet( + myCustomRoleId, + false, + instanceOwner); + + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, false); + + // THEN + assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); + + // WHEN - activate + vm.expectEmit(address(instance)); + emit IInstance.LogInstanceCustomRoleActiveSet( + myCustomRoleId, + true, + instanceOwner); + + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, true); + + // THEN + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); + } + + + function test_instanceAuthzRolesSetActiveHappyCase2() public { + // GIVEN setup + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + + // WHEN - activate active role + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, true); + + // THEN + assertTrue(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected paused at timestamp"); + + // WHEN - deactivate active role + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, false); + + // THEN + assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); + + // WHEN - deactivate pused role + vm.prank(instanceOwner); + instance.setRoleActive(myCustomRoleId, false); + + // THEN + assertFalse(instanceReader.isRoleActive(myCustomRoleId), "role not active"); + assertEq(instanceReader.getRoleInfo(myCustomRoleId).pausedAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected paused at timestamp"); + } + + + function test_instanceAuthzRolesSetActiveRoleNonexistent() public { + // GIVEN setup + RoleId fakeRoleId = RoleIdLib.toRoleId(12345); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotCustomRole.selector, + fakeRoleId)); + + vm.prank(instanceOwner); + instance.setRoleActive(fakeRoleId, false); + } + + + function test_instanceAuthzRolesSetActiveRoleNotCustom() public { + // GIVEN setup + RoleId instanceRoleId = instance.getInstanceAdmin().getRoleForName("InstanceRole"); + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotCustomRole.selector, + instanceRoleId)); + + vm.prank(instanceOwner); + instance.setRoleActive(instanceRoleId, false); + } + + + function test_instanceAuthzRolesSetActiveNotInstanceOwner() public { + // GIVEN + address someAccount = makeAddr("someAccount"); + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotRoleAdmin.selector, + myCustomRoleId, + someAccount)); + + vm.prank(someAccount); + instance.setRoleActive(myCustomRoleId, false); + } + + //--- role granting ----------------------------------------------------// + + function test_instanceAuthzRolesGrantInstancOwnerHappyCase() public { + // GIVEN + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + address someAccount = makeAddr("someAccount"); + + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount), "some account unexpectedly role member"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 0, "unexpected role member count"); + + // WHEN + vm.expectEmit(address(instance)); + emit IInstance.LogInstanceCustomRoleGranted( + myCustomRoleId, + someAccount, + instanceOwner); + + vm.prank(instanceOwner); + instance.grantRole(myCustomRoleId, someAccount); + + // THEN + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount), "some account not role member"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 1, "unexpected role member count"); + assertEq(instanceReader.getRoleMember(myCustomRoleId, 0), someAccount, "unexpected role member"); + } + + + function test_instanceAuthzRolesGrantSomeAccountHappyCase() public { + // GIVEN + RoleId myCustomAdminRoleId = _createRole("MyCustomAdminRole", INSTANCE_OWNER_ROLE(), 1); + RoleId myCustomRoleId = _createRole("MyCustomRole", myCustomAdminRoleId, 42); + address someAdminAccount = makeAddr("someAdminAccount"); + address someAccount1 = makeAddr("someAccount1"); + address someAccount2 = makeAddr("someAccount2"); + address instanceOwnerColleague = makeAddr("instanceOwnerColleague"); + + assertFalse(instanceReader.isRoleMember(myCustomAdminRoleId, someAdminAccount), "some admin account unexpectedly role member (before)"); + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount1), "some account1 unexpectedly role member (before)"); + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount2), "some account2 unexpectedly role member (before)"); + assertFalse(instanceReader.isRoleMember(myCustomRoleId, instanceOwnerColleague), "instance owner colleague unexpectedly role member (before)"); + assertEq(instanceReader.roleMembers(myCustomAdminRoleId), 0, "unexpected admin role member count (before)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 0, "unexpected role member count (before)"); + + // WHEN grantings by instance owner + vm.startPrank(instanceOwner); + instance.grantRole(myCustomAdminRoleId, someAdminAccount); + instance.grantRole(myCustomRoleId, instanceOwnerColleague); + vm.stopPrank(); + + // THEN + assertTrue(instanceReader.isRoleMember(myCustomAdminRoleId, someAdminAccount), "some admin account not role member (after1)"); + assertTrue(instanceReader.isRoleAdmin(myCustomRoleId, someAdminAccount), "some admin account not admin role member (after1)"); + + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount1), "some account1 unexpectedly role member (after1)"); + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount2), "some account2 unexpectedly role member (after1)"); + assertTrue(instanceReader.isRoleMember(myCustomRoleId, instanceOwnerColleague), "instance owner colleague not role member (after1)"); + assertEq(instanceReader.roleMembers(myCustomAdminRoleId), 1, "unexpected admin role member count (after1)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 1, "unexpected role member count (after1)"); + + assertTrue(instanceReader.isRoleAdmin(myCustomRoleId, someAdminAccount), "some admin account not role admin"); + + // WHEN grantings by some admin account + vm.startPrank(someAdminAccount); + instance.grantRole(myCustomRoleId, someAccount1); + instance.grantRole(myCustomRoleId, someAccount2); + vm.stopPrank(); + + // THEN + assertTrue(instanceReader.isRoleMember(myCustomAdminRoleId, someAdminAccount), "some admin account not role member (after2)"); + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount1), "some account1 not role member (after2)"); + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount2), "some account2 not role member (after2)"); + assertTrue(instanceReader.isRoleMember(myCustomRoleId, instanceOwnerColleague), "instance owner colleague not role member (after2)"); + assertEq(instanceReader.roleMembers(myCustomAdminRoleId), 1, "unexpected admin role member count (after2)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 3, "unexpected role member count (after2)"); + } + + + function test_instanceAuthzRolesGrantInactiveRole() public { + // GIVEN + RoleId customRoleId = _createRole("CustomRole", INSTANCE_OWNER_ROLE(), 3); + address someAccount = makeAddr("someAccount"); + + // pause role + vm.prank(instanceOwner); + instance.setRoleActive(customRoleId, false); + + assertFalse(instanceReader.isRoleMember(customRoleId, someAccount), "unexpected role member (before)"); + assertEq(instanceReader.roleMembers(customRoleId), 0, "unexpected role member count (before)"); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IAccessAdmin.ErrorAccessAdminRoleIsPaused.selector, + customRoleId)); + + vm.prank(instanceOwner); + instance.grantRole(customRoleId, someAccount); + + // THEN nothing happened + assertFalse(instanceReader.isRoleMember(customRoleId, someAccount), "unexpected role member (after)"); + assertEq(instanceReader.roleMembers(customRoleId), 0, "unexpected role member count (after)"); + } + + + function test_instanceAuthzRolesGrantMaxMemberExceeded() public { + // GIVEN + uint32 maxMemberCount = 3; + RoleId customRoleId = _createRole("CustomRole", INSTANCE_OWNER_ROLE(), maxMemberCount); + address someAccount = makeAddr("someAccount"); + + assertFalse(instanceReader.isRoleMember(customRoleId, someAccount), "some account unexpectedly role member (before)"); + assertEq(instanceReader.roleMembers(customRoleId), 0, "unexpected role member count (before)"); + + // WHEN multiple grantings to same account + vm.startPrank(instanceOwner); + instance.grantRole(customRoleId, someAccount); + instance.grantRole(customRoleId, someAccount); + instance.grantRole(customRoleId, someAccount); + instance.grantRole(customRoleId, someAccount); + vm.stopPrank(); + + // THEN everything ok so far + assertTrue(instanceReader.isRoleMember(customRoleId, someAccount), "some account not role member (after1)"); + assertEq(instanceReader.roleMembers(customRoleId), 1, "unexpected role member count (after1)"); + assertEq(instanceReader.getRoleMember(customRoleId, 0), someAccount, "unexpected 1st role member (after1)"); + + // WHEN reach max member count + address someAccount1 = makeAddr("someAccount1"); + address someAccount2 = makeAddr("someAccount2"); + + vm.startPrank(instanceOwner); + instance.grantRole(customRoleId, someAccount1); + instance.grantRole(customRoleId, someAccount2); + vm.stopPrank(); + + // THEN still ok + assertTrue(instanceReader.isRoleMember(customRoleId, someAccount), "some account not role member (after2)"); + assertTrue(instanceReader.isRoleMember(customRoleId, someAccount1), "some account1 not role member (after2)"); + assertTrue(instanceReader.isRoleMember(customRoleId, someAccount2), "some account2 not role member (after2)"); + assertEq(instanceReader.roleMembers(customRoleId), 3, "unexpected role member count (after2)"); + assertEq(instanceReader.getRoleMember(customRoleId, 0), someAccount, "unexpected 1st role member (after2)"); + assertEq(instanceReader.getRoleMember(customRoleId, 1), someAccount1, "unexpected 2nd role member (after2)"); + assertEq(instanceReader.getRoleMember(customRoleId, 2), someAccount2, "unexpected 3rd role member (after2)"); + + // WHEN exceed max member count + address someAccount3 = makeAddr("someAccount3"); + + vm.expectRevert( + abi.encodeWithSelector( + IAccessAdmin.ErrorAccessAdminRoleMembersLimitReached.selector, + customRoleId, + maxMemberCount)); + + vm.prank(instanceOwner); + instance.grantRole(customRoleId, someAccount3); + + // THEN + assertEq(instanceReader.roleMembers(customRoleId), 3, "unexpected role member count (after2)"); + } + + + function test_instanceAuthzRolesGrantNotInstanceOwner() public { + // GIVEN + uint32 maxMemberCount = 3; + RoleId customRoleId = _createRole("CustomRole", INSTANCE_OWNER_ROLE(), maxMemberCount); + address someAccount = makeAddr("someAccount"); + + assertFalse(instanceReader.isRoleMember(customRoleId, someAccount), "some account unexpectedly role member (before)"); + assertEq(instanceReader.roleMembers(customRoleId), 0, "unexpected role member count (before)"); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotRoleAdmin.selector, + customRoleId, + someAccount)); + + vm.prank(someAccount); + instance.grantRole(customRoleId, someAccount); + + // THEN nothing happened + assertEq(instanceReader.roleMembers(customRoleId), 0, "unexpected role member count (after)"); + assertFalse(instanceReader.isRoleMember(customRoleId, someAccount), "some account unexpectedly role member (after)"); + } + + + function test_instanceAuthzRolesGrantNotRoleAdmin() public { + // GIVEN + RoleId myCustomAdminRoleId = _createRole("MyCustomAdminRole", INSTANCE_OWNER_ROLE(), 1); + RoleId myCustomRoleId = _createRole("MyCustomRole", myCustomAdminRoleId, 42); + address someAdminAccount = makeAddr("someAdminAccount"); + address someAccount = makeAddr("someAccount"); + + vm.prank(instanceOwner); + instance.grantRole(myCustomAdminRoleId, someAdminAccount); + + assertTrue(instanceReader.isRoleAdmin(myCustomRoleId, someAdminAccount), "some admin account not role admin"); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotRoleAdmin.selector, + myCustomRoleId, + someAccount)); + + vm.prank(someAccount); + instance.grantRole(myCustomRoleId, someAccount); + } + + function test_instanceAuthzRolesGrantRoleNonexisting() public { + // GIVEN + RoleId fakeRoleId = RoleIdLib.toRoleId(12345); + address someAccount = makeAddr("someAccount"); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotCustomRole.selector, + fakeRoleId)); + + vm.prank(instanceOwner); + instance.grantRole(fakeRoleId, someAccount); + } + + + function test_instanceAuthzRolesGrantRoleNotCustomRole() public { + // GIVEN + RoleId instanceRoleId = instanceAdmin.getRoleForName("InstanceRole"); + address someAccount = makeAddr("someAccount"); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotCustomRole.selector, + instanceRoleId)); + + vm.prank(instanceOwner); + instance.grantRole(instanceRoleId, someAccount); + } + + + //--- role revoking ----------------------------------------------------// + + function test_instanceAuthzRolesRevokeInstancOwnerHappyCase() public { + // GIVEN + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + address someAccount = makeAddr("someAccount"); + + vm.prank(instanceOwner); + instance.grantRole(myCustomRoleId, someAccount); + + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount), "some account not role member (before)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 1, "unexpected role member count (before)"); + + // WHEN + THEN + vm.expectEmit(address(instance)); + emit IInstance.LogInstanceCustomRoleRevoked( + myCustomRoleId, + someAccount, + instanceOwner); + + vm.prank(instanceOwner); + instance.revokeRole(myCustomRoleId, someAccount); + + // THEN + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount), "some account unexpectedly role member (after)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 0, "unexpected role member count (after)"); + } + + //--- helper functions ----------------------------------------------------// + + function _createRole(string memory roleName, RoleId adminRole, uint32 maxMemberCount) internal returns (RoleId) { + vm.prank(instanceOwner); + return instance.createRole(roleName, adminRole, maxMemberCount); + } + + + function _printRoles() internal { + // print roles + for(uint256 i = 0; i < instanceReader.roles(); i++) { + RoleId roleId = instanceReader.getRoleId(i); + IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(roleId); + console.log("role", i, roleId.toInt(), roleInfo.name.toString()); + } + } +} \ No newline at end of file diff --git a/test/release/ReleaseRegistryConcrete.t.sol b/test/release/ReleaseRegistryConcrete.t.sol index 58e75a51c..b136e2462 100644 --- a/test/release/ReleaseRegistryConcrete.t.sol +++ b/test/release/ReleaseRegistryConcrete.t.sol @@ -662,7 +662,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { assertEq(serviceNftId.toInt(), expectedNftId, "registerService() return unexpected value"); ReleaseAdmin releaseAdmin = ReleaseAdmin(nextReleaseInfo.releaseAdmin); assertTrue(AccessManagerCloneable(releaseAdmin.authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(releaseAdmin.isRoleMember(address(service), expectedServiceRoleId), "isRoleMember() return unexpected value"); + assertTrue(releaseAdmin.isRoleMember(expectedServiceRoleId, address(service)), "isRoleMember() return unexpected value"); assertTrue(releaseAdmin.targetExists(address(service)), "targetExists() return unexpected value"); assertTrue(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #2"); assertFalse(registryAdmin.targetExists(address(service)), "targetExists() return unexpected value"); @@ -701,7 +701,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { ReleaseAdmin( nextReleaseInfo.releaseAdmin).authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(registryAdmin.isRoleMember(address(service), expectedServiceRoleIdForAllVersions), "isRoleMember() return unexpected value"); + assertTrue(registryAdmin.isRoleMember(expectedServiceRoleIdForAllVersions, address(service)), "isRoleMember() return unexpected value"); _assert_releaseRegistry_getters(nextVersion, nextReleaseInfo); _assert_releaseRegistry_getters(prevVersion, prevReleaseInfo); @@ -823,7 +823,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { ReleaseAdmin releaseAdmin = ReleaseAdmin(nextReleaseInfo.releaseAdmin); assertTrue(AccessManagerCloneable(releaseAdmin.authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(releaseAdmin.isRoleMember(address(service), expectedServiceRoleId), "isRoleMember() return unexpected value"); + assertTrue(releaseAdmin.isRoleMember(expectedServiceRoleId, address(service)), "isRoleMember() return unexpected value"); assertTrue(releaseAdmin.targetExists(address(service)), "targetExists() return unexpected value"); assertTrue(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #2"); assertFalse(registryAdmin.targetExists(address(service)), "targetExists() return unexpected value"); @@ -854,7 +854,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { // check activation assertFalse(AccessManagerCloneable(ReleaseAdmin(nextReleaseInfo.releaseAdmin) .authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(registryAdmin.isRoleMember(address(service), expectedServiceRoleIdForAllVersions), "isRoleMember() return unexpected value"); + assertTrue(registryAdmin.isRoleMember(expectedServiceRoleIdForAllVersions, address(service)), "isRoleMember() return unexpected value"); _assert_releaseRegistry_getters(nextVersion, nextReleaseInfo); _assert_releaseRegistry_getters(prevVersion, prevReleaseInfo); @@ -989,7 +989,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { assertTrue(AccessManagerCloneable(ReleaseAdmin(nextReleaseInfo.releaseAdmin) .authority()).isLocked(), "isLocked() return unexpected value"); assertTrue(ReleaseAdmin(nextReleaseInfo.releaseAdmin) - .isRoleMember(address(service), expectedServiceRoleId), "isRoleMember() return unexpected value"); + .isRoleMember(expectedServiceRoleId, address(service)), "isRoleMember() return unexpected value"); assertTrue(ReleaseAdmin(nextReleaseInfo.releaseAdmin) .targetExists(address(service)), "targetExists() return unexpected value"); assertTrue(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #2"); @@ -1021,7 +1021,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { // check activation assertFalse(AccessManagerCloneable(ReleaseAdmin(nextReleaseInfo.releaseAdmin) .authority()).isLocked(), "isLocked() return unexpected value"); - assertTrue(registryAdmin.isRoleMember(address(service), expectedServiceRoleIdForAllVersions), "isRoleMember() return unexpected value"); + assertTrue(registryAdmin.isRoleMember(expectedServiceRoleIdForAllVersions, address(service)), "isRoleMember() return unexpected value"); _assert_releaseRegistry_getters(nextVersion, nextReleaseInfo); _assert_releaseRegistry_getters(prevVersion, prevReleaseInfo); From f4dc0f21ce02c06d34096620436fde6889164e36 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Wed, 28 Aug 2024 15:23:25 +0000 Subject: [PATCH 05/18] add role revoking --- .../authorization/InstanceAuthzRoles.t.sol | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/test/instance/authorization/InstanceAuthzRoles.t.sol b/test/instance/authorization/InstanceAuthzRoles.t.sol index 336577c0f..9e3d7da90 100644 --- a/test/instance/authorization/InstanceAuthzRoles.t.sol +++ b/test/instance/authorization/InstanceAuthzRoles.t.sol @@ -555,6 +555,81 @@ contract InstanceAuthzRolesTest is GifTest { assertEq(instanceReader.roleMembers(myCustomRoleId), 0, "unexpected role member count (after)"); } + + function test_instanceAuthzRolesRevokeRoleAdminHappyCase() public { + // GIVEN + RoleId myCustomAdminRoleId = _createRole("MyCustomAdminRole", INSTANCE_OWNER_ROLE(), 1); + RoleId myCustomRoleId = _createRole("MyCustomRole", myCustomAdminRoleId, 42); + address someAdminAccount = makeAddr("someAdminAccount"); + address someAccount1 = makeAddr("someAccount1"); + address someAccount2 = makeAddr("someAccount2"); + + vm.prank(instanceOwner); + instance.grantRole(myCustomAdminRoleId, someAdminAccount); + + vm.startPrank(someAdminAccount); + instance.grantRole(myCustomRoleId, someAccount1); + instance.grantRole(myCustomRoleId, someAccount2); + vm.stopPrank(); + + assertTrue(instanceReader.isRoleMember(myCustomAdminRoleId, someAdminAccount), "some admin account unexpectedly role member (before)"); + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount1), "some account1 unexpectedly role member (before)"); + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount2), "some account2 unexpectedly role member (before)"); + assertEq(instanceReader.roleMembers(myCustomAdminRoleId), 1, "unexpected admin role member count (before)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 2, "unexpected role member count (before)"); + + // WHEN revoke by role admin + vm.prank(someAdminAccount); + instance.revokeRole(myCustomRoleId, someAccount1); + + // THEN + assertTrue(instanceReader.isRoleMember(myCustomAdminRoleId, someAdminAccount), "some admin account unexpectedly role member (after1)"); + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount1), "some account1 unexpectedly role member (after1)"); + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount2), "some account2 unexpectedly role member (after1)"); + assertEq(instanceReader.roleMembers(myCustomAdminRoleId), 1, "unexpected admin role member count (after1)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 1, "unexpected role member count (after1)"); + + // WHEN revoke by instance owner + vm.prank(instanceOwner); + instance.revokeRole(myCustomRoleId, someAccount2); + + // THEN + assertTrue(instanceReader.isRoleMember(myCustomAdminRoleId, someAdminAccount), "some admin account unexpectedly role member (after2)"); + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount1), "some account1 unexpectedly role member (after2)"); + assertFalse(instanceReader.isRoleMember(myCustomRoleId, someAccount2), "some account2 unexpectedly role member (after2)"); + assertEq(instanceReader.roleMembers(myCustomAdminRoleId), 1, "unexpected admin role member count (after2)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 0, "unexpected role member count (after2)"); + } + + + function test_instanceAuthzRolesRevokeNotRoleAdmin() public { + // GIVEN + RoleId myCustomRoleId = _createRole("MyCustomRole", INSTANCE_OWNER_ROLE(), 42); + address someAdminAccount = makeAddr("someAdminAccount"); + address someAccount = makeAddr("someAccount"); + + vm.prank(instanceOwner); + instance.grantRole(myCustomRoleId, someAccount); + + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount), "some account not role member (before)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 1, "unexpected role member count (before)"); + + // WHEN + THEN + vm.expectRevert( + abi.encodeWithSelector( + IInstance.ErrorInstanceNotRoleAdmin.selector, + myCustomRoleId, + someAdminAccount)); + + vm.prank(someAdminAccount); + instance.revokeRole(myCustomRoleId, someAccount); + + // THEN nothing happened + assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount), "some account not role member (before)"); + assertEq(instanceReader.roleMembers(myCustomRoleId), 1, "unexpected role member count (before)"); + } + + //--- helper functions ----------------------------------------------------// function _createRole(string memory roleName, RoleId adminRole, uint32 maxMemberCount) internal returns (RoleId) { From 69709f76e4fb9b217adcfe4721c5aa8ec1ab1ce7 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Wed, 28 Aug 2024 20:48:00 +0000 Subject: [PATCH 06/18] add custom target creation --- contracts/authorization/AccessAdmin.sol | 34 +++---- contracts/authorization/AccessAdminLib.sol | 16 ++- contracts/authorization/Authorization.sol | 4 +- contracts/authorization/IAccessAdmin.sol | 42 +------- .../authorization/IServiceAuthorization.sol | 2 +- .../authorization/ServiceAuthorization.sol | 4 +- .../distribution/DistributionService.sol | 46 ++++++++- .../distribution/IDistributionComponent.sol | 4 - .../distribution/IDistributionService.sol | 14 ++- contracts/instance/IInstance.sol | 11 ++- contracts/instance/IInstanceService.sol | 16 +-- contracts/instance/Instance.sol | 20 ++-- contracts/instance/InstanceAdmin.sol | 40 +++++++- .../instance/InstanceAuthorizationV3.sol | 3 +- contracts/instance/InstanceReader.sol | 99 ++++++++++++++----- contracts/instance/InstanceService.sol | 51 ++++++---- contracts/registry/RegistryAdmin.sol | 6 +- contracts/registry/ReleaseAdmin.sol | 2 +- contracts/registry/ServiceAuthorizationV3.sol | 3 +- contracts/staking/TargetManagerLib.sol | 2 +- test/Component.t.sol | 2 +- test/GifDeployer.t.sol | 14 +-- test/TestDeployAll.t.sol | 7 +- test/TestPool.t.sol | 2 +- test/TestProduct.t.sol | 2 +- test/authorization/AccessAdmin.t.sol | 1 + test/base/GifTest.sol | 10 +- .../authorization/InstanceAuthzBase.t.sol | 47 +++++++++ .../authorization/InstanceAuthzRoles.t.sol | 23 +---- .../authorization/InstanceAuthzTargets.t.sol | 71 +++++++++++++ test/mock/ContractV01.sol | 2 +- test/mock/ContractV02.sol | 2 +- test/staking/Staking.t.sol | 20 ++-- test/staking/TargetManagement.t.sol | 8 +- 34 files changed, 427 insertions(+), 203 deletions(-) create mode 100644 test/instance/authorization/InstanceAuthzBase.t.sol create mode 100644 test/instance/authorization/InstanceAuthzTargets.t.sol diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index 2b144ecfd..f96418e95 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -234,7 +234,7 @@ contract AccessAdmin is return _roleForName[StrLib.toStr(name)].roleId; } - function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory) { + function getRoleInfo(RoleId roleId) public view returns (RoleInfo memory) { return _roleInfo[roleId]; } @@ -702,21 +702,21 @@ contract AccessAdmin is } } + // TODO cleanup + // function _checkIsRegistered( + // address registry, + // address target, + // ObjectType expectedType + // ) + // internal + // view + // { + // AccessAdminLib.checkIsRegistered(registry, target, expectedType); + // } - function _checkIsRegistered( - address registry, - address target, - ObjectType expectedType - ) - internal - view - { - AccessAdminLib.checkIsRegistered(registry, target, expectedType); - } - - function _checkRegistry(address registry) internal view { - if (!ContractLib.isRegistry(registry)) { - revert ErrorAccessAdminNotRegistry(registry); - } - } + // function _checkRegistry(address registry) internal view { + // if (!ContractLib.isRegistry(registry)) { + // revert ErrorAccessAdminNotRegistry(registry); + // } + // } } \ No newline at end of file diff --git a/contracts/authorization/AccessAdminLib.sol b/contracts/authorization/AccessAdminLib.sol index e4a351f17..64696789e 100644 --- a/contracts/authorization/AccessAdminLib.sol +++ b/contracts/authorization/AccessAdminLib.sol @@ -24,6 +24,7 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB IAccess.FunctionInfo[] memory functions ) public + pure returns ( bytes4[] memory selectors ) @@ -113,7 +114,6 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB revert IAccessAdmin.ErrorAccessAdminTargetAuthorityMismatch(accessAdmin.authority(), targetAuthority); } } - } @@ -160,6 +160,8 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB public view { + checkRegistry(registry); + ObjectType tagetType = IRegistry(registry).getObjectInfo(target).objectType; if (tagetType.eqz()) { revert IAccessAdmin.ErrorAccessAdminNotRegistered(target); @@ -170,6 +172,18 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB } } + + function checkRegistry( + address registry + ) + public + view + { + if (!ContractLib.isRegistry(registry)) { + revert IAccessAdmin.ErrorAccessAdminNotRegistry(registry); + } + } + function toRole( RoleId adminRoleId, IAccessAdmin.RoleType roleType, diff --git a/contracts/authorization/Authorization.sol b/contracts/authorization/Authorization.sol index e12467d3e..a89e6b225 100644 --- a/contracts/authorization/Authorization.sol +++ b/contracts/authorization/Authorization.sol @@ -107,7 +107,7 @@ contract Authorization is return _serviceDomains; } - function getComponentRole(ObjectType componentDomain) public view returns(RoleId roleId) { + function getComponentRole(ObjectType componentDomain) public pure returns(RoleId roleId) { return RoleIdLib.toComponentRoleId(componentDomain, 0); } @@ -149,7 +149,7 @@ contract Authorization is return _tokenHandlerTarget; } - function getTarget(string memory targetName) public view returns(Str target) { + function getTarget(string memory targetName) public pure returns(Str target) { return StrLib.toStr(targetName); } diff --git a/contracts/authorization/IAccessAdmin.sol b/contracts/authorization/IAccessAdmin.sol index be9adc2d0..622ab642c 100644 --- a/contracts/authorization/IAccessAdmin.sol +++ b/contracts/authorization/IAccessAdmin.sol @@ -14,6 +14,7 @@ import {RoleId} from "../type/RoleId.sol"; import {Str} from "../type/String.sol"; import {VersionPart} from "../type/Version.sol"; +/// @dev Base interface for registry admin, release admin, and instance admin interface IAccessAdmin is IAccessManaged, IAccess, @@ -95,47 +96,6 @@ interface IAccessAdmin is // check target error ErrorAccessAdminTargetUnknown(address target); - /// @dev Set the disabled status of the speicified role. - /// Role disabling only prevents the role from being granted to new accounts. - /// Existing role members may still execute functions that are authorized for that role. - /// Permissioned: the caller must have the manager role (getManagerRole). - // TODO move to instance admin - // function setRoleDisabled(RoleId roleId, bool disabled) external; - - /// @dev Grant the specified account the provided role. - /// Permissioned: the caller must have the roles admin role. - // TODO move to instance admin - // function grantRole(address account, RoleId roleId) external; - - /// @dev Revoke the provided role from the specified account. - /// Permissioned: the caller must have the roles admin role. - // TODO move to instance admin - // function revokeRole(address account, RoleId roleId) external; - - /// @dev Removes the provided role from the caller - // TODO move to instance admin - // function renounceRole(RoleId roleId) external; - - /// @dev Set the locked status of the speicified contract. - /// IMPORTANT: using this function the AccessManager might itself be put into locked state from which it cannot be unlocked again. - /// Overwrite this function if a different use case specific behaviour is required. - /// Alternatively, add specific function to just unlock this contract without a restricted() modifier. - /// Permissioned: the caller must have the manager role (getManagerRole). - // TODO move to instance admin - // function setTargetLocked(address target, bool locked) external; - - /// @dev Specifies which functions of the target can be accessed by the provided role. - /// Previously existing authorizations will be overwritten. - /// Authorizing the admin role is not allowed, use function unauthorizedFunctions for this. - /// Permissioned: the caller must have the manager role (getManagerRole). - // TODO move to instance admin - // function authorizeFunctions(address target, RoleId roleId, FunctionInfo[] memory functions) external; - - /// @dev Specifies for which functionss to remove any previous authorization - /// Permissioned: the caller must have the manager role (getManagerRole). - // TODO move to instance admin - // function unauthorizeFunctions(address target, FunctionInfo[] memory functions) external; - //--- view functions ----------------------------------------------------// function getAuthorization() external view returns (IAuthorization authorization); diff --git a/contracts/authorization/IServiceAuthorization.sol b/contracts/authorization/IServiceAuthorization.sol index c19d2760e..1b0e14d65 100644 --- a/contracts/authorization/IServiceAuthorization.sol +++ b/contracts/authorization/IServiceAuthorization.sol @@ -22,7 +22,7 @@ interface IServiceAuthorization is IERC165 { returns(VersionPart release); /// @dev Returns the service domain for the provided index. - function getServiceDomain(uint idx) external view returns(ObjectType serviceDomain); + function getServiceDomain(uint256 idx) external view returns(ObjectType serviceDomain); /// @dev Returns the full list of service domains for this release. /// Services need to be registered for the release in revers order of this list. diff --git a/contracts/authorization/ServiceAuthorization.sol b/contracts/authorization/ServiceAuthorization.sol index db210f577..c460d03e8 100644 --- a/contracts/authorization/ServiceAuthorization.sol +++ b/contracts/authorization/ServiceAuthorization.sol @@ -48,7 +48,7 @@ contract ServiceAuthorization is return _serviceDomains; } - function getServiceDomain(uint idx) external view returns(ObjectType serviceDomain) { + function getServiceDomain(uint256 idx) external view returns(ObjectType serviceDomain) { return _serviceDomains[idx]; } @@ -74,9 +74,11 @@ contract ServiceAuthorization is /// @dev Overwrite this function for a specific realease. + // solhint-disable-next-line no-empty-blocks function _setupDomains() internal virtual {} /// @dev Overwrite this function for a specific realease. + // solhint-disable-next-line no-empty-blocks function _setupDomainAuthorizations() internal virtual {} /// @dev Use this method to to add an authorized domain. diff --git a/contracts/distribution/DistributionService.sol b/contracts/distribution/DistributionService.sol index a245b8bdd..5eb0e299b 100644 --- a/contracts/distribution/DistributionService.sol +++ b/contracts/distribution/DistributionService.sol @@ -20,10 +20,10 @@ import {KEEP_STATE} from "../type/StateId.sol"; import {ObjectType, ACCOUNTING, COMPONENT, DISTRIBUTION, INSTANCE, DISTRIBUTION, DISTRIBUTOR, REGISTRY} from "../type/ObjectType.sol"; import {InstanceReader} from "../instance/InstanceReader.sol"; import {InstanceStore} from "../instance/InstanceStore.sol"; -import {ReferralId, ReferralLib} from "../type/Referral.sol"; +import {ReferralId, ReferralStatus, ReferralLib, REFERRAL_OK, REFERRAL_ERROR_UNKNOWN, REFERRAL_ERROR_EXPIRED, REFERRAL_ERROR_EXHAUSTED} from "../type/Referral.sol"; import {Seconds} from "../type/Seconds.sol"; import {Timestamp, TimestampLib} from "../type/Timestamp.sol"; -import {UFixed} from "../type/UFixed.sol"; +import {UFixed, UFixedLib} from "../type/UFixed.sol"; contract DistributionService is @@ -342,6 +342,48 @@ contract DistributionService is isValid = isValid && info.usedReferrals < info.maxReferrals; } + + function getDiscountPercentage( + InstanceReader instanceReader, + ReferralId referralId + ) + external + virtual + view + returns ( + UFixed discountPercentage, + ReferralStatus status + ) + { + IDistribution.ReferralInfo memory info = instanceReader.getReferralInfo( + referralId); + + if (info.expiryAt.eqz()) { + return ( + UFixedLib.zero(), + REFERRAL_ERROR_UNKNOWN()); + } + + if (info.expiryAt < TimestampLib.blockTimestamp()) { + return ( + UFixedLib.zero(), + REFERRAL_ERROR_EXPIRED()); + } + + if (info.usedReferrals >= info.maxReferrals) { + return ( + UFixedLib.zero(), + REFERRAL_ERROR_EXHAUSTED()); + } + + return ( + info.discountPercentage, + REFERRAL_OK() + ); + + } + + function _getInstanceForDistribution(NftId distributionNftId) internal view diff --git a/contracts/distribution/IDistributionComponent.sol b/contracts/distribution/IDistributionComponent.sol index 9df7a6f7c..d4d4a21e8 100644 --- a/contracts/distribution/IDistributionComponent.sol +++ b/contracts/distribution/IDistributionComponent.sol @@ -20,10 +20,6 @@ interface IDistributionComponent is IInstanceLinkedComponent { /// @dev Returns the distributor Nft Id for the provided address function getDistributorNftId(address distributor) external view returns (NftId distributorNftId); - function getDiscountPercentage( - string memory referralCode - ) external view returns (UFixed discountPercentage, ReferralStatus status); - function getReferralId( string memory referralCode ) external returns (ReferralId referralId); diff --git a/contracts/distribution/IDistributionService.sol b/contracts/distribution/IDistributionService.sol index cd729ff23..802007846 100644 --- a/contracts/distribution/IDistributionService.sol +++ b/contracts/distribution/IDistributionService.sol @@ -1,15 +1,17 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {Amount} from "../type/Amount.sol"; -import {NftId} from "../type/NftId.sol"; import {IPolicy} from "../instance/module/IPolicy.sol"; import {IService} from "../shared/IService.sol"; -import {UFixed} from "../type/UFixed.sol"; + +import {Amount} from "../type/Amount.sol"; import {DistributorType} from "../type/DistributorType.sol"; -import {ReferralId} from "../type/Referral.sol"; +import {InstanceReader} from "../instance/InstanceReader.sol"; +import {NftId} from "../type/NftId.sol"; +import {ReferralId, ReferralStatus} from "../type/Referral.sol"; import {Seconds} from "../type/Seconds.sol"; import {Timestamp} from "../type/Timestamp.sol"; +import {UFixed} from "../type/UFixed.sol"; interface IDistributionService is IService { @@ -98,4 +100,8 @@ interface IDistributionService is IService { /// @param amount the amount to withdraw. If set to AMOUNT_MAX, the full commission available is withdrawn /// @return withdrawnAmount the effective withdrawn amount function withdrawCommission(NftId distributorNftId, Amount amount) external returns (Amount withdrawnAmount); + + /// @dev Returns the discount percentage for the provided referral code. + /// The function retuns both the percentage and the status of the referral code. + function getDiscountPercentage(InstanceReader instanceReader, ReferralId referralId) external view returns (UFixed discountPercentage, ReferralStatus status); } \ No newline at end of file diff --git a/contracts/instance/IInstance.sol b/contracts/instance/IInstance.sol index 2e94c3653..051794ae9 100644 --- a/contracts/instance/IInstance.sol +++ b/contracts/instance/IInstance.sol @@ -23,6 +23,11 @@ interface IInstance is event LogInstanceCustomRoleGranted(RoleId roleId, address account, address caller); event LogInstanceCustomRoleRevoked(RoleId roleId, address account, address caller); + // target handling + event LogInstanceCustomTargetCreated(address target, RoleId targetRoleId, string name); + event LogInstanceTargetLocked(address target, bool locked); + event LogInstanceCustomTargetFunctionRoleSet(address target, bytes4[] selectors, RoleId roleId); + // modifier is onlyRoleAdmin error ErrorInstanceNotCustomRole(RoleId roleId); error ErrorInstanceNotRoleAdmin(RoleId roleId, address account); @@ -102,7 +107,11 @@ interface IInstance is /// Only instance owner or account with role admin role can call this function. function revokeRole(RoleId roleId, address account) external; - function createTarget(address target, string memory name) external; + /// @dev Creates a new custom target. + /// Custom targets are intended to be used for access control helper contracts of components. + /// Custom targets are not intended to be used for components. + function createTarget(address target, RoleId targetRoleId, string memory name) external; + function setTargetFunctionRole(string memory targetName, bytes4[] calldata selectors, RoleId roleId) external; diff --git a/contracts/instance/IInstanceService.sol b/contracts/instance/IInstanceService.sol index 7e41922c7..d7895e11f 100644 --- a/contracts/instance/IInstanceService.sol +++ b/contracts/instance/IInstanceService.sol @@ -54,24 +54,28 @@ interface IInstanceService is IService { event LogInstanceCloned(NftId instanceNftId, address instance); - /// @dev creates a new role for the calling instance. + /// @dev Creates a new custom role for the calling instance. function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId); - /// @dev sets the specified role as active or inactive for the calling instance. + /// @dev Sets the specified custom role as active or inactive for the calling instance. function setRoleActive(RoleId roleId, bool active) external; - /// @dev grants the specified role to the specified account for the calling instance. + /// @dev Grants the specified custom role to the specified account for the calling instance. function grantRole(RoleId roleId, address account) external; - /// @dev revokes the specified role from the specified account for the calling instance. + /// @dev Revokes the specified custom role from the specified account for the calling instance. function revokeRole(RoleId roleId, address account) external; - /// @dev Locks the complete instance, including all its components. - function setInstanceLocked(bool locked) external; + /// @dev Creates a new custom target for the calling instance. + /// Optionally, a target role can be specified that will be assigned to the target. + function createTarget(address target, RoleId targetRoleId, string memory name) external; /// @dev Locks/unlocks the specified target constrolled by the corresponding instance admin. function setTargetLocked(address target, bool locked) external; + /// @dev Locks the complete instance, including all its components. + function setInstanceLocked(bool locked) external; + /// @dev Creates a new instance. /// The caller becomes the owner of the new instance. /// Creation of a new instance is achieved by this service through the creation and registration diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index 62793e141..a95a4053b 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -226,24 +226,28 @@ contract Instance is //--- Targets ------------------------------------------------------------// - - function setTargetLocked(address target, bool locked) + /// @inheritdoc IInstance + function createTarget(address target, RoleId targetRoleId, string memory name) external - // not restricted(): instance owner may need to be able to unlock targets on an locked instance + restricted() onlyOwner() { - _instanceService.setTargetLocked(target, locked); + _instanceService.createTarget(target, targetRoleId, name); + emit LogInstanceCustomTargetCreated(target, targetRoleId, name); } - function createTarget(address target, string memory name) + + /// @inheritdoc IInstance + function setTargetLocked(address target, bool locked) external - restricted() + // not restricted(): instance owner may need to be able to unlock targets on an locked instance onlyOwner() { - // TODO refactor - // _instanceAdmin.createTarget(target, name); + _instanceService.setTargetLocked(target, locked); + emit LogInstanceTargetLocked(target, locked); } + function setTargetFunctionRole( string memory targetName, bytes4[] calldata selectors, diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index ff2101cb1..8083716f6 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -30,8 +30,9 @@ contract InstanceAdmin is error ErrorInstanceAdminNotInstanceService(address caller); + error ErrorInstanceAdminNotCustomRole(RoleId roleId); + error ErrorInstanceAdminNotRegistered(address instance); - error ErrorInstanceAdminNotInstance(address instance); error ErrorInstanceAdminAlreadyAuthorized(address instance); error ErrorInstanceAdminNotComponentRole(RoleId roleId); @@ -49,6 +50,7 @@ contract InstanceAdmin is mapping(address target => RoleId roleId) internal _targetRoleId; uint64 internal _components; + modifier onlyInstanceService() { if (msg.sender != _registry.getServiceAddress(INSTANCE(), getRelease())) { revert ErrorInstanceAdminNotInstanceService(msg.sender); @@ -78,8 +80,7 @@ contract InstanceAdmin is onlyDeployer() { // checks - _checkRegistry(registry); - _checkIsRegistered(registry, instance, INSTANCE()); + AccessAdminLib.checkIsRegistered(registry, instance, INSTANCE()); AccessManagerCloneable( authority()).completeSetup( @@ -105,7 +106,6 @@ contract InstanceAdmin is // add instance authorization _createRoles(_authorization); - // _createTargets(_authorization); _setupInstanceHelperTargetsWithRoles(); _createTargetAuthorizations(_authorization); } @@ -131,7 +131,7 @@ contract InstanceAdmin is restricted() { // checks - _checkIsRegistered(address(getRegistry()), componentAddress, expectedType); + AccessAdminLib.checkIsRegistered(address(getRegistry()), componentAddress, expectedType); IInstanceLinkedComponent component = IInstanceLinkedComponent(componentAddress); IAuthorization authorization = component.getAuthorization(); @@ -180,6 +180,7 @@ contract InstanceAdmin is instance); } + /// @dev Creates a custom role. function createRole( string memory roleName, @@ -220,6 +221,7 @@ contract InstanceAdmin is _grantRoleToAccount(roleId, account); } + /// @dev Revokes the provided role from the specified account function revokeRole( RoleId roleId, @@ -231,6 +233,34 @@ contract InstanceAdmin is } + /// @dev Create a new custom target. + /// The target needs to be an access managed contract. + /// The target role parameter is optional, set to zero if not required. + /// Only custom roles may be assigned to custom targets. + function createTarget( + address target, + RoleId targetRoleId, + string memory name + ) + external + restricted() + { + _createTarget( + target, + name, + true, // checkAuthority + true); // custom + + if (targetRoleId != RoleIdLib.zero()) { + if (getRoleInfo(targetRoleId).roleType != IAccess.RoleType.Custom) { + revert ErrorInstanceAdminNotCustomRole(targetRoleId); + } + + _grantRoleToAccount(targetRoleId, target); + } + } + + function setInstanceLocked(bool locked) external // not restricted(): need to operate on locked instances to unlock instance diff --git a/contracts/instance/InstanceAuthorizationV3.sol b/contracts/instance/InstanceAuthorizationV3.sol index a7d2fe27b..795d04956 100644 --- a/contracts/instance/InstanceAuthorizationV3.sol +++ b/contracts/instance/InstanceAuthorizationV3.sol @@ -161,8 +161,9 @@ contract InstanceAuthorizationV3 _authorize(functions, InstanceAdmin.grantRole.selector, "grantRole"); _authorize(functions, InstanceAdmin.revokeRole.selector, "revokeRole"); - _authorize(functions, InstanceAdmin.setInstanceLocked.selector, "setInstanceLocked"); + _authorize(functions, InstanceAdmin.createTarget.selector, "createTarget"); _authorize(functions, InstanceAdmin.setTargetLocked.selector, "setTargetLocked"); + _authorize(functions, InstanceAdmin.setInstanceLocked.selector, "setInstanceLocked"); // authorize component service role functions = _authorizeForTarget(INSTANCE_ADMIN_TARGET_NAME, getServiceRole(COMPONENT())); diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index c6fd169fc..041440484 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -7,6 +7,7 @@ import {IAccess} from "../authorization/IAccess.sol"; import {IBundle} from "../instance/module/IBundle.sol"; import {IComponents} from "../instance/module/IComponents.sol"; import {IDistribution} from "../instance/module/IDistribution.sol"; +import {IDistributionService} from "../distribution/IDistributionService.sol"; import {IInstance} from "./IInstance.sol"; import {IKeyValueStore} from "../shared/IKeyValueStore.sol"; import {IOracle} from "../oracle/IOracle.sol"; @@ -24,7 +25,7 @@ import {InstanceStore} from "./InstanceStore.sol"; import {Key32} from "../type/Key32.sol"; import {NftId} from "../type/NftId.sol"; import {PayoutId, PayoutIdLib} from "../type/PayoutId.sol"; -import {ReferralId, ReferralStatus, ReferralLib, REFERRAL_OK, REFERRAL_ERROR_UNKNOWN, REFERRAL_ERROR_EXPIRED, REFERRAL_ERROR_EXHAUSTED} from "../type/Referral.sol"; +import {ReferralId, ReferralStatus, ReferralLib} from "../type/Referral.sol"; import {RequestId} from "../type/RequestId.sol"; import {RiskId} from "../type/RiskId.sol"; import {RiskSet} from "./RiskSet.sol"; @@ -208,7 +209,7 @@ contract InstanceReader { function getClaimId(uint idx) public - view + pure returns (ClaimId claimId) { return ClaimIdLib.toClaimId(idx + 1); @@ -246,7 +247,7 @@ contract InstanceReader { function getPayoutId(ClaimId claimId, uint24 idx) public - view + pure returns (PayoutId payoutId) { return PayoutIdLib.toPayoutId(claimId, idx + 1); @@ -497,6 +498,7 @@ contract InstanceReader { } } + function getMetadata(Key32 key) public view @@ -505,6 +507,7 @@ contract InstanceReader { return _store.getMetadata(key); } + function getState(Key32 key) public view @@ -536,31 +539,39 @@ contract InstanceReader { ReferralStatus status ) { - IDistribution.ReferralInfo memory info = getReferralInfo( - referralId); + return IDistributionService( + _registry.getServiceAddress( + DISTRIBUTION(), + _instance.getRelease())).getDiscountPercentage( + this, // instance reader + referralId); - if (info.expiryAt.eqz()) { - return ( - UFixedLib.zero(), - REFERRAL_ERROR_UNKNOWN()); - } + // TODO cleanup + // IDistribution.ReferralInfo memory info = getReferralInfo( + // referralId); - if (info.expiryAt < TimestampLib.blockTimestamp()) { - return ( - UFixedLib.zero(), - REFERRAL_ERROR_EXPIRED()); - } + // if (info.expiryAt.eqz()) { + // return ( + // UFixedLib.zero(), + // REFERRAL_ERROR_UNKNOWN()); + // } - if (info.usedReferrals >= info.maxReferrals) { - return ( - UFixedLib.zero(), - REFERRAL_ERROR_EXHAUSTED()); - } + // if (info.expiryAt < TimestampLib.blockTimestamp()) { + // return ( + // UFixedLib.zero(), + // REFERRAL_ERROR_EXPIRED()); + // } - return ( - info.discountPercentage, - REFERRAL_OK() - ); + // if (info.usedReferrals >= info.maxReferrals) { + // return ( + // UFixedLib.zero(), + // REFERRAL_ERROR_EXHAUSTED()); + // } + + // return ( + // info.discountPercentage, + // REFERRAL_OK() + // ); } @@ -569,7 +580,7 @@ contract InstanceReader { } - function getInstanceOwnerRole() public view returns (RoleId roleId) { + function getInstanceOwnerRole() public pure returns (RoleId roleId) { return INSTANCE_OWNER_ROLE(); } @@ -596,14 +607,17 @@ contract InstanceReader { return _instance.getInstanceAdmin().isRoleActive(roleId); } + function roleMembers(RoleId roleId) public view returns (uint256 numberOfMembers) { return _instance.getInstanceAdmin().roleMembers(roleId); } + function getRoleMember(RoleId roleId, uint256 idx) public view returns (address account) { return _instance.getInstanceAdmin().getRoleMember(roleId, idx); } + function isRoleMember(RoleId roleId, address account) public view returns (bool isMember) { return _instance.getInstanceAdmin().isRoleMember(roleId, account); } @@ -614,64 +628,97 @@ contract InstanceReader { } + function targets() public view returns (uint256 targetCount) { + return _instance.getInstanceAdmin().targets(); + } + + + function getTargetAddress(uint256 idx) public view returns (address target) { + return _instance.getInstanceAdmin().getTargetAddress(idx); + } + + + function targetExists(address target) public view returns (bool exists) { + return _instance.getInstanceAdmin().targetExists(target); + } + + + function getTargetInfo(address target) public view returns (IAccess.TargetInfo memory targetInfo) { + return _instance.getInstanceAdmin().getTargetInfo(target); + } + + function isLocked(address target) public view returns (bool) { return _instance.getInstanceAdmin().isTargetLocked(target); } + function toPolicyKey(NftId policyNftId) public pure returns (Key32) { return policyNftId.toKey32(POLICY()); } + function toPremiumKey(NftId policyNftId) public pure returns (Key32) { return policyNftId.toKey32(PREMIUM()); } + function toDistributorKey(NftId distributorNftId) public pure returns (Key32) { return distributorNftId.toKey32(DISTRIBUTOR()); } + function toBundleKey(NftId poolNftId) public pure returns (Key32) { return poolNftId.toKey32(BUNDLE()); } + function toComponentKey(NftId componentNftId) public pure returns (Key32) { return componentNftId.toKey32(COMPONENT()); } + function toDistributionKey(NftId distributionNftId) public pure returns (Key32) { return distributionNftId.toKey32(DISTRIBUTION()); } + function toPoolKey(NftId poolNftId) public pure returns (Key32) { return poolNftId.toKey32(POOL()); } + function toProductKey(NftId productNftId) public pure returns (Key32) { return productNftId.toKey32(PRODUCT()); } + function toFeeKey(NftId productNftId) public pure returns (Key32) { return productNftId.toKey32(FEE()); } - // low level function + //--- low level function ----------------------------------------------------// function getInstanceStore() external view returns (IKeyValueStore store) { return _store; } + function getBundleSet() external view returns (BundleSet bundleSet) { return _bundleSet; } + function getRiskSet() external view returns (RiskSet riskSet) { return _riskSet; } + function toUFixed(uint256 value, int8 exp) public pure returns (UFixed) { return UFixedLib.toUFixed(value, exp); } + function toInt(UFixed value) public pure returns (uint256) { return UFixedLib.toInt(value); } diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index 311375215..440143efb 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -56,21 +56,22 @@ contract InstanceService is } - modifier onlyInstanceOwner(NftId instanceNftId) { - if(msg.sender != getRegistry().ownerOf(instanceNftId)) { - revert ErrorInstanceServiceRequestUnauhorized(msg.sender); - } - _; - } - + // TODO cleanup + // modifier onlyInstanceOwner(NftId instanceNftId) { + // if(msg.sender != getRegistry().ownerOf(instanceNftId)) { + // revert ErrorInstanceServiceRequestUnauhorized(msg.sender); + // } + // _; + // } + + // // TODO check component - service - instance version match + // modifier onlyComponent() { + // if (! getRegistry().isRegisteredComponent(msg.sender)) { + // revert ErrorInstanceServiceRequestUnauhorized(msg.sender); + // } + // _; + // } - // TODO check component - service - instance version match - modifier onlyComponent() { - if (! getRegistry().isRegisteredComponent(msg.sender)) { - revert ErrorInstanceServiceRequestUnauhorized(msg.sender); - } - _; - } /// @inheritdoc IInstanceService function createRole( @@ -90,6 +91,7 @@ contract InstanceService is maxMemberCount); } + /// @inheritdoc IInstanceService function setRoleActive(RoleId roleId, bool active) external @@ -111,6 +113,7 @@ contract InstanceService is instance.getInstanceAdmin().grantRole(roleId, account); } + /// @inheritdoc IInstanceService function revokeRole(RoleId roleId, address account) external @@ -121,15 +124,15 @@ contract InstanceService is instance.getInstanceAdmin().revokeRole(roleId, account); } + /// @inheritdoc IInstanceService - function setInstanceLocked(bool locked) + function createTarget(address target, RoleId targetRoleId, string memory name) external - virtual restricted() onlyInstance() { - address instanceAddress = msg.sender; - IInstance(instanceAddress).getInstanceAdmin().setInstanceLocked(locked); + IInstance instance = IInstance(msg.sender); + instance.getInstanceAdmin().createTarget(target, targetRoleId, name); } @@ -145,6 +148,18 @@ contract InstanceService is } + /// @inheritdoc IInstanceService + function setInstanceLocked(bool locked) + external + virtual + restricted() + onlyInstance() + { + address instanceAddress = msg.sender; + IInstance(instanceAddress).getInstanceAdmin().setInstanceLocked(locked); + } + + function createInstance() external virtual diff --git a/contracts/registry/RegistryAdmin.sol b/contracts/registry/RegistryAdmin.sol index 99a25ec89..2a13cd358 100644 --- a/contracts/registry/RegistryAdmin.sol +++ b/contracts/registry/RegistryAdmin.sol @@ -91,7 +91,7 @@ contract RegistryAdmin is onlyDeployer() { // checks - _checkRegistry(registry); + AccessAdminLib.checkRegistry(registry); VersionPart release = VersionPartLib.toVersionPart(3); AccessManagerCloneable( @@ -141,11 +141,11 @@ contract RegistryAdmin is //--- view functions ----------------------------------------------------// - function getGifAdminRole() external view returns (RoleId) { + function getGifAdminRole() external pure returns (RoleId) { return GIF_ADMIN_ROLE(); } - function getGifManagerRole() external view returns (RoleId) { + function getGifManagerRole() external pure returns (RoleId) { return GIF_MANAGER_ROLE(); } diff --git a/contracts/registry/ReleaseAdmin.sol b/contracts/registry/ReleaseAdmin.sol index 503c11f05..dd556e833 100644 --- a/contracts/registry/ReleaseAdmin.sol +++ b/contracts/registry/ReleaseAdmin.sol @@ -57,7 +57,7 @@ contract ReleaseAdmin is { // checks - _checkRegistry(registry); + AccessAdminLib.checkRegistry(registry); AccessManagerCloneable( authority()).completeSetup( diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index f367fcbcb..032a8ce18 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -134,8 +134,9 @@ contract ServiceAuthorizationV3 _authorize(functions, IInstanceService.grantRole.selector, "grantRole"); _authorize(functions, IInstanceService.revokeRole.selector, "revokeRole"); - _authorize(functions, IInstanceService.setInstanceLocked.selector, "setInstanceLocked"); + _authorize(functions, IInstanceService.createTarget.selector, "createTarget"); _authorize(functions, IInstanceService.setTargetLocked.selector, "setTargetLocked"); + _authorize(functions, IInstanceService.setInstanceLocked.selector, "setInstanceLocked"); _authorize(functions, IInstanceService.createInstance.selector, "createInstance"); _authorize(functions, IInstanceService.upgradeInstanceReader.selector, "upgradeInstanceReader"); diff --git a/contracts/staking/TargetManagerLib.sol b/contracts/staking/TargetManagerLib.sol index e55ea85eb..963395d87 100644 --- a/contracts/staking/TargetManagerLib.sol +++ b/contracts/staking/TargetManagerLib.sol @@ -160,7 +160,7 @@ library TargetManagerLib { UFixed stakingRate ) public - view + pure returns (Amount dipAmount) { dipAmount = tokenAmount.multiplyWith(stakingRate); diff --git a/test/Component.t.sol b/test/Component.t.sol index b6f14760d..b906369c2 100644 --- a/test/Component.t.sol +++ b/test/Component.t.sol @@ -28,7 +28,7 @@ contract TestComponent is GifTest { _printAuthz(instance.getInstanceAdmin(), "instance (including components)"); } - function test_componentGetComponentInfo() public { + function test_componentGetComponentInfo() public view { // GIVEN - just setUp IComponents.ComponentInfo memory componentInfo = instanceReader.getComponentInfo(distributionNftId); diff --git a/test/GifDeployer.t.sol b/test/GifDeployer.t.sol index 0a5e999b5..02cfaf690 100644 --- a/test/GifDeployer.t.sol +++ b/test/GifDeployer.t.sol @@ -47,13 +47,13 @@ contract GifDeployerTest is GifDeployer { address public stakingOwner = registryOwner; - function test_deployerCoreDip() public { + function test_deployerCoreDip() public view { assertTrue(address(dip) != address(0), "dip address zero"); assertEq(dip.decimals(), 18, "unexpected decimals for dip"); } - function test_deployerCoreRegistry() public { + function test_deployerCoreRegistry() public view { assertTrue(address(registry) != address(0), "registry address zero"); // check registry @@ -81,7 +81,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreTokenRegistry() public { + function test_deployerCoreTokenRegistry() public view { assertTrue(address(tokenRegistry) != address(0), "token registry address zero"); assertEq(address(tokenRegistry.getDipToken()), address(dip), "unexpected dip address"); @@ -92,7 +92,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreReleaseRegistry() public { + function test_deployerCoreReleaseRegistry() public view { assertTrue(address(releaseRegistry) != address(0), "release manager address zero"); // check authority @@ -136,7 +136,7 @@ contract GifDeployerTest is GifDeployer { // TODO amend once full gif setup is streamlined } - function test_deployerCoreStakingManager() public { + function test_deployerCoreStakingManager() public view { assertTrue(address(stakingManager) != address(0), "staking manager address zero"); // assertEq(stakingOwner, registryOwner, "unexpected staking owner"); @@ -146,7 +146,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreStakingContract() public { + function test_deployerCoreStakingContract() public view { assertTrue(address(staking) != address(0), "staking address zero"); // check nft id @@ -184,7 +184,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreStakingStore() public { + function test_deployerCoreStakingStore() public view { StakingStore store = StakingStore(staking.getStakingStore()); // check authority diff --git a/test/TestDeployAll.t.sol b/test/TestDeployAll.t.sol index cb1be8982..8bd441ff9 100644 --- a/test/TestDeployAll.t.sol +++ b/test/TestDeployAll.t.sol @@ -69,7 +69,7 @@ contract TestDeployAll is GifTest { } - function test_deployServicesOverview() public { + function test_deployServicesOverview() public view { assertEq(registry.getObjectCount(), 25, "invalid object count for base setup"); // validate registry service @@ -139,7 +139,7 @@ contract TestDeployAll is GifTest { assertTrue(address(instanceReader) != address(0), "instance reader is zero address"); } - function test_deployAllInstanceOwner() public { + function test_deployAllInstanceOwner() public view { NftId nftId = registry.getNftIdForAddress(address(instance)); assertEq( registry.ownerOf(nftId), @@ -148,7 +148,7 @@ contract TestDeployAll is GifTest { ); } - function test_deployAllInstanceLifecycles() public { + function test_deployAllInstanceLifecycles() public view { assertTrue(instance.getInstanceStore().hasLifecycle(BUNDLE()), "instance misses bundle lifecycle"); assertTrue(instance.getInstanceStore().hasLifecycle(COMPONENT()), "instance misses component lifecycle"); assertTrue(instance.getInstanceStore().hasLifecycle(POLICY()), "instance misses policy lifecycle"); @@ -166,6 +166,7 @@ contract TestDeployAll is GifTest { address componentOwner ) internal + view { // check params against unexpected 0 values assertTrue(address(component) != address(0), "component address 0"); diff --git a/test/TestPool.t.sol b/test/TestPool.t.sol index e4f9ee7a6..336ef85da 100644 --- a/test/TestPool.t.sol +++ b/test/TestPool.t.sol @@ -59,7 +59,7 @@ contract TestPool is GifTest { getLocationHash("etherisc.storage.Oracle"); } - function getLocationHash(string memory location) public returns (bytes32 locationHash) { + function getLocationHash(string memory location) public view returns (bytes32 locationHash) { locationHash = pool.getContractLocation(bytes(location)); // solhint-disable console.log(location); diff --git a/test/TestProduct.t.sol b/test/TestProduct.t.sol index bebd100cc..c7f459b8c 100644 --- a/test/TestProduct.t.sol +++ b/test/TestProduct.t.sol @@ -38,7 +38,7 @@ contract TestProduct is GifTest { sec30 = SecondsLib.toSeconds(30); } - function test_productSetupInfo() public { + function test_productSetupInfo() public view { // check nft id (components -> product) uint256 productNftIdInt = product.getNftId().toInt(); diff --git a/test/authorization/AccessAdmin.t.sol b/test/authorization/AccessAdmin.t.sol index 181cd357c..ca1932491 100644 --- a/test/authorization/AccessAdmin.t.sol +++ b/test/authorization/AccessAdmin.t.sol @@ -1147,6 +1147,7 @@ contract AccessAdminTest is AccessAdminBaseTest { Timestamp expectedCreatedAt ) internal + view { // solhint-disable-next-line console.log("checking role", expectedName); diff --git a/test/base/GifTest.sol b/test/base/GifTest.sol index 44936cb12..45725faa6 100644 --- a/test/base/GifTest.sol +++ b/test/base/GifTest.sol @@ -197,7 +197,7 @@ contract GifTest is GifDeployer { } /// @dev Helper function to assert that a given NftId is equal to the expected NftId. - function assertNftId(NftId actualNftId, NftId expectedNftId, string memory message) public { + function assertNftId(NftId actualNftId, NftId expectedNftId, string memory message) public view { if(block.chainid == 31337) { assertEq(actualNftId.toInt(), expectedNftId.toInt(), message); } else { @@ -207,7 +207,7 @@ contract GifTest is GifDeployer { } /// @dev Helper function to assert that a given NftId is equal to zero. - function assertNftIdZero(NftId nftId, string memory message) public { + function assertNftIdZero(NftId nftId, string memory message) public view { if(block.chainid == 31337) { assertTrue(nftId.eqz(), message); } else { @@ -517,7 +517,7 @@ contract GifTest is GifDeployer { function _getSimpleProductInfo() internal - view + pure returns (IComponents.ProductInfo memory productInfo) { return IComponents.ProductInfo({ @@ -534,7 +534,7 @@ contract GifTest is GifDeployer { function _getSimpleFeeInfo() internal - view + pure returns (IComponents.FeeInfo memory feeInfo) { return IComponents.FeeInfo({ @@ -730,7 +730,7 @@ contract GifTest is GifDeployer { assertTrue(nftId1.eq(nftId2), message); } - function assertEq(Timestamp ts1, Timestamp ts2, string memory message) internal { + function assertEq(Timestamp ts1, Timestamp ts2, string memory message) internal pure { assertEq(ts1.toInt(), ts2.toInt(), message); } } \ No newline at end of file diff --git a/test/instance/authorization/InstanceAuthzBase.t.sol b/test/instance/authorization/InstanceAuthzBase.t.sol new file mode 100644 index 000000000..6b40cf301 --- /dev/null +++ b/test/instance/authorization/InstanceAuthzBase.t.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {console} from "../../../lib/forge-std/src/Test.sol"; + +import {IAccess} from "../../../contracts/authorization/IAccess.sol"; +import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; +import {IInstance} from "../../../contracts/instance/IInstance.sol"; +import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; + +import {ComponentService} from "../../../contracts/shared/ComponentService.sol"; +import {GifTest} from "../../base/GifTest.sol"; +import {FeeLib} from "../../../contracts/type/Fee.sol"; +import {NftId, NftIdLib} from "../../../contracts/type/NftId.sol"; +import {RoleId,RoleIdLib, ADMIN_ROLE, INSTANCE_OWNER_ROLE} from "../../../contracts/type/RoleId.sol"; +import {SimplePool} from "../../../contracts/examples/unpermissioned/SimplePool.sol"; +import {SimpleProduct} from "../../../contracts/examples/unpermissioned/SimpleProduct.sol"; +import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; +import {UFixedLib} from "../../../contracts/type/UFixed.sol"; + +contract InstanceAuthzBaseTest is GifTest { + + //--- helper functions ----------------------------------------------------// + + function _createRole(string memory roleName, RoleId adminRole, uint32 maxMemberCount) internal returns (RoleId) { + vm.prank(instanceOwner); + return instance.createRole(roleName, adminRole, maxMemberCount); + } + + + function _printRoles() internal { + for(uint256 i = 0; i < instanceReader.roles(); i++) { + RoleId roleId = instanceReader.getRoleId(i); + IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(roleId); + console.log("role", i, roleId.toInt(), roleInfo.name.toString()); + } + } + + + function _printTargets() internal { + for(uint256 i = 0; i < instanceReader.targets(); i++) { + address target = instanceReader.getTargetAddress(i); + IAccess.TargetInfo memory targetInfo = instanceReader.getTargetInfo(target); + console.log("target", i, target, targetInfo.name.toString()); + } + } +} \ No newline at end of file diff --git a/test/instance/authorization/InstanceAuthzRoles.t.sol b/test/instance/authorization/InstanceAuthzRoles.t.sol index 9e3d7da90..642c2b05f 100644 --- a/test/instance/authorization/InstanceAuthzRoles.t.sol +++ b/test/instance/authorization/InstanceAuthzRoles.t.sol @@ -11,6 +11,7 @@ import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; import {ComponentService} from "../../../contracts/shared/ComponentService.sol"; import {GifTest} from "../../base/GifTest.sol"; import {FeeLib} from "../../../contracts/type/Fee.sol"; +import {InstanceAuthzBaseTest} from "./InstanceAuthzBase.t.sol"; import {NftId, NftIdLib} from "../../../contracts/type/NftId.sol"; import {RoleId,RoleIdLib, ADMIN_ROLE, INSTANCE_OWNER_ROLE} from "../../../contracts/type/RoleId.sol"; import {SimplePool} from "../../../contracts/examples/unpermissioned/SimplePool.sol"; @@ -18,9 +19,9 @@ import {SimpleProduct} from "../../../contracts/examples/unpermissioned/SimplePr import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; import {UFixedLib} from "../../../contracts/type/UFixed.sol"; -contract InstanceAuthzRolesTest is GifTest { +contract InstanceAuthzRolesTest is InstanceAuthzBaseTest { - function test_instanceAuthzSetup() public { + function test_instanceAuthzRolesSetup() public { _printRoles(); // check initial roles @@ -628,22 +629,4 @@ contract InstanceAuthzRolesTest is GifTest { assertTrue(instanceReader.isRoleMember(myCustomRoleId, someAccount), "some account not role member (before)"); assertEq(instanceReader.roleMembers(myCustomRoleId), 1, "unexpected role member count (before)"); } - - - //--- helper functions ----------------------------------------------------// - - function _createRole(string memory roleName, RoleId adminRole, uint32 maxMemberCount) internal returns (RoleId) { - vm.prank(instanceOwner); - return instance.createRole(roleName, adminRole, maxMemberCount); - } - - - function _printRoles() internal { - // print roles - for(uint256 i = 0; i < instanceReader.roles(); i++) { - RoleId roleId = instanceReader.getRoleId(i); - IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(roleId); - console.log("role", i, roleId.toInt(), roleInfo.name.toString()); - } - } } \ No newline at end of file diff --git a/test/instance/authorization/InstanceAuthzTargets.t.sol b/test/instance/authorization/InstanceAuthzTargets.t.sol new file mode 100644 index 000000000..f0a7880ef --- /dev/null +++ b/test/instance/authorization/InstanceAuthzTargets.t.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {console} from "../../../lib/forge-std/src/Test.sol"; + +import {AccessManagedMock} from "../../mock/AccessManagedMock.sol"; +import {IAccess} from "../../../contracts/authorization/IAccess.sol"; +import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; +import {IInstance} from "../../../contracts/instance/IInstance.sol"; +import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; + +import {InstanceAuthzBaseTest} from "./InstanceAuthzBase.t.sol"; +import {RoleId, RoleIdLib, ADMIN_ROLE, INSTANCE_OWNER_ROLE} from "../../../contracts/type/RoleId.sol"; +import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; + + +contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { + + function test_instanceAuthzTargetsSetup() public { + _printRoles(); + _printTargets(); + + // check initial roles + assertEq(instanceAdmin.targets(), 16, "unexpected initial instance target count (admin)"); + assertEq(instanceReader.targets(), 16, "unexpected initial instance target count (reader)"); + } + + + function test_instanceAuthzTargetsCreateNoRoleHappyCase() public { + // GIVEN + AccessManagedMock target = _deployAccessManagedMock(); + RoleId zeroRoleId = RoleIdLib.zero(); + string memory targetName = "MyTarget"; + uint256 initialTargetCount = instanceAdmin.targets(); + + // WHEN + THEN + vm.expectEmit(address(instance)); + emit IInstance.LogInstanceCustomTargetCreated(address(target), zeroRoleId, targetName); + + vm.prank(instanceOwner); + instance.createTarget(address(target), zeroRoleId, targetName); + + // THEN + assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); + assertEq(instanceAdmin.targets(), initialTargetCount + 1, "unexpected target count after create (admin)"); + assertEq(instanceReader.targets(), initialTargetCount + 1, "unexpected target count after create (reader)"); + + IAccess.TargetInfo memory targetInfo = instanceReader.getTargetInfo(address(target)); + assertEq(targetInfo.name.toString(), "MyTarget", "unexpected target name"); + assertTrue(targetInfo.isCustom, "target type not custom"); + assertEq(targetInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected target creation time"); + } + + + function test_instanceAuthzTargetsCreateWithRoleHappyCase() public { + } + + + function test_instanceAuthzTargetsSetTargetLockedHappyCase() public { + } + + //--- helper functions ----------------------------------------------------// + + function _deployAccessManagedMock() internal returns (AccessManagedMock accessManagedMock) { + return _deployAccessManagedMock(instance.authority()); + } + + function _deployAccessManagedMock(address authority) internal returns (AccessManagedMock accessManagedMock) { + accessManagedMock = new AccessManagedMock(instance.authority()); + } +} \ No newline at end of file diff --git a/test/mock/ContractV01.sol b/test/mock/ContractV01.sol index eaa60247b..b72a08f21 100644 --- a/test/mock/ContractV01.sol +++ b/test/mock/ContractV01.sol @@ -27,7 +27,7 @@ contract ContractV01 is Versionable { function getDataV01() external - view + pure returns(bytes memory) { return "hi from version 1"; diff --git a/test/mock/ContractV02.sol b/test/mock/ContractV02.sol index 2e2daa568..c4940edd3 100644 --- a/test/mock/ContractV02.sol +++ b/test/mock/ContractV02.sol @@ -23,7 +23,7 @@ contract ContractV02 is ContractV01 { return VersionLib.toVersion(1, 0, 1); } - function getDataV02() external view returns(bytes memory) { + function getDataV02() external pure returns(bytes memory) { return "hi from version 2"; } diff --git a/test/staking/Staking.t.sol b/test/staking/Staking.t.sol index 82ba93318..a987bc22b 100644 --- a/test/staking/Staking.t.sol +++ b/test/staking/Staking.t.sol @@ -336,7 +336,6 @@ contract StakingTest is GifTest { UFixed rewardRate = stakingReader.getTargetInfo(instanceNftId).rewardRate; ( Amount rewardIncrease, - Amount totalDipAmount ) = StakeManagerLib.calculateRewardIncrease( stakingReader, stakeNftId, @@ -391,7 +390,7 @@ contract StakingTest is GifTest { uint256 lastUpdateAt = block.timestamp; - (, Amount reserveAmount,) = _addRewardReserves(instanceNftId, instanceOwner, 500); + (, Amount reserveAmount) = _addRewardReserves(instanceNftId, instanceOwner, 500); assertEq(stakingReader.getReserveBalance(instanceNftId).toInt(), reserveAmount.toInt(), "unexpected reserve balance (initial)"); // dip balance of staker after staking @@ -447,15 +446,11 @@ contract StakingTest is GifTest { function test_stakingStakeUnstake_stakeLocked() public { // GIVEN - ( - , - Amount stakeAmount, - NftId stakeNftId - ) = _prepareStake(staker, instanceNftId, 1000); + (,, NftId stakeNftId) = _prepareStake(staker, instanceNftId, 1000); uint256 lastUpdateAt = block.timestamp; - (, Amount reserveAmount,) = _addRewardReserves(instanceNftId, instanceOwner, 500); + (, Amount reserveAmount) = _addRewardReserves(instanceNftId, instanceOwner, 500); assertEq(stakingReader.getReserveBalance(instanceNftId).toInt(), reserveAmount.toInt(), "unexpected reserve balance (initial)"); // dip balance of staker after staking @@ -485,7 +480,7 @@ contract StakingTest is GifTest { uint256 lastUpdateAt = block.timestamp; - (, Amount reserveAmount,) = _addRewardReserves(instanceNftId, instanceOwner, 500); + (, Amount reserveAmount) = _addRewardReserves(instanceNftId, instanceOwner, 500); assertEq(stakingReader.getReserveBalance(instanceNftId).toInt(), reserveAmount.toInt(), "unexpected reserve balance (initial)"); // dip balance of staker after staking @@ -546,7 +541,7 @@ contract StakingTest is GifTest { uint256 lastUpdateAt = block.timestamp; - (, Amount reserveAmount,) = _addRewardReserves(instanceNftId, instanceOwner, 10); + (, Amount reserveAmount) = _addRewardReserves(instanceNftId, instanceOwner, 10); assertEq(stakingReader.getReserveBalance(instanceNftId).toInt(), reserveAmount.toInt(), "unexpected reserve balance (initial)"); // dip balance of staker after staking @@ -582,15 +577,14 @@ contract StakingTest is GifTest { function _addRewardReserves( - NftId instanceNftId, + NftId, address instanceOwner, uint256 amount ) internal returns( TokenHandler tokenHandler, - Amount dipAmount, - NftId stakeNftId + Amount dipAmount ) { (tokenHandler, dipAmount) = _prepareAccount(instanceOwner, amount); diff --git a/test/staking/TargetManagement.t.sol b/test/staking/TargetManagement.t.sol index c2feb99f0..345a4412f 100644 --- a/test/staking/TargetManagement.t.sol +++ b/test/staking/TargetManagement.t.sol @@ -115,10 +115,7 @@ contract StakingTargetManagementTest is GifTest { address stakingWallet = staking.getWallet(); uint256 refillAmountFullDips = 500; - ( - TokenHandler tokenHandler, - Amount refillAmount - ) = _prepareAccount(instanceOwner, refillAmountFullDips); + (, Amount refillAmount) = _prepareAccount(instanceOwner, refillAmountFullDips); assertEq(dip.balanceOf(stakingWallet), 0, "staking wallet dip balance not 0 (after instance owner funding)"); assertEq(refillAmount.toInt(), refillAmountFullDips * 10 ** dip.decimals(), "unexpected refill amount"); @@ -208,7 +205,6 @@ contract StakingTargetManagementTest is GifTest { function test_stakingTargetWithdrawRewardReservesOutsider() public { // GIVEN - address stakingWallet = staking.getWallet(); uint256 refillAmountFullDips = 500; (, Amount refillAmount) = _addRewardReserves(instanceNftId, instanceOwner, refillAmountFullDips); @@ -239,7 +235,7 @@ contract StakingTargetManagementTest is GifTest { function _addRewardReserves( - NftId instanceNftId, + NftId, address account, uint256 amount ) From 528b9f62d3cf64a99d025c13345af16833e823cf Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Wed, 28 Aug 2024 21:49:56 +0000 Subject: [PATCH 07/18] reduce InstanceReader size --- .../distribution/IDistributionComponent.sol | 9 ++++---- contracts/instance/InstanceReader.sol | 14 +++++++------ contracts/product/PolicyServiceLib.sol | 21 +++++++++++++++++++ scripts/libs/instance.ts | 2 +- test/staking/Staking.t.sol | 2 +- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/contracts/distribution/IDistributionComponent.sol b/contracts/distribution/IDistributionComponent.sol index 1fb4f254a..cbbaee3ef 100644 --- a/contracts/distribution/IDistributionComponent.sol +++ b/contracts/distribution/IDistributionComponent.sol @@ -11,11 +11,12 @@ interface IDistributionComponent is IInstanceLinkedComponent { event LogDistributorUpdated(address to, address operator); - /// @dev Returns true iff the provided address is registered as a distributor with this distribution component. - function isDistributor(address candidate) external view returns (bool); + // TODO cleanup + // /// @dev Returns true iff the provided address is registered as a distributor with this distribution component. + // function isDistributor(address candidate) external view returns (bool); - /// @dev Returns the distributor Nft Id for the provided address - function getDistributorNftId(address distributor) external view returns (NftId distributorNftId); + // /// @dev Returns the distributor Nft Id for the provided address + // function getDistributorNftId(address distributor) external view returns (NftId distributorNftId); function getDiscountPercentage( string memory referralCode diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index 6e59ae211..3d5ebdce4 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -25,6 +25,7 @@ import {InstanceStore} from "./InstanceStore.sol"; import {Key32} from "../type/Key32.sol"; import {NftId} from "../type/NftId.sol"; import {PayoutId, PayoutIdLib} from "../type/PayoutId.sol"; +import {PolicyServiceLib} from "../product/PolicyServiceLib.sol"; import {ReferralId, ReferralStatus, ReferralLib} from "../type/Referral.sol"; import {RequestId} from "../type/RequestId.sol"; import {RiskId} from "../type/RiskId.sol"; @@ -190,14 +191,15 @@ contract InstanceReader { view returns (bool isCloseable) { - IPolicy.PolicyInfo memory info = getPolicyInfo(policyNftId); + // TODO cleanup + // IPolicy.PolicyInfo memory info = getPolicyInfo(policyNftId); - if (info.productNftId.eqz()) { return false; } // not closeable: policy does not exist (or does not belong to this instance) - if (info.activatedAt.eqz()) { return false; } // not closeable: not yet activated - if (info.activatedAt > TimestampLib.blockTimestamp()) { return false; } // not yet active - if (info.expiredAt <= TimestampLib.blockTimestamp()) { return false; } // already expired + // if (info.productNftId.eqz()) { return false; } // not closeable: policy does not exist (or does not belong to this instance) + // if (info.activatedAt.eqz()) { return false; } // not closeable: not yet activated + // if (info.activatedAt > TimestampLib.blockTimestamp()) { return false; } // not yet active + // if (info.expiredAt <= TimestampLib.blockTimestamp()) { return false; } // already expired - return true; + return PolicyServiceLib.policyIsActive(this, policyNftId); } function claims(NftId policyNftId) diff --git a/contracts/product/PolicyServiceLib.sol b/contracts/product/PolicyServiceLib.sol index eada3f0e2..f5e71202d 100644 --- a/contracts/product/PolicyServiceLib.sol +++ b/contracts/product/PolicyServiceLib.sol @@ -10,6 +10,27 @@ import {Timestamp, TimestampLib} from "../type/Timestamp.sol"; library PolicyServiceLib { + function policyIsActive(InstanceReader instanceReader, NftId policyNftId) + external + view + returns (bool isActive) + { + // policy not collateralized + if (instanceReader.getPolicyState(policyNftId) != COLLATERALIZED()) { + return false; + } + + IPolicy.PolicyInfo memory info = instanceReader.getPolicyInfo(policyNftId); + + if (info.productNftId.eqz()) { return false; } // not closeable: policy does not exist (or does not belong to this instance) + if (info.activatedAt.eqz()) { return false; } // not closeable: not yet activated + if (info.activatedAt > TimestampLib.blockTimestamp()) { return false; } // not yet active + if (info.expiredAt <= TimestampLib.blockTimestamp()) { return false; } // already expired + + return true; + } + + function checkExpiration( Timestamp newExpiredAt, NftId policyNftId, diff --git a/scripts/libs/instance.ts b/scripts/libs/instance.ts index d84d5c2e9..951209b36 100644 --- a/scripts/libs/instance.ts +++ b/scripts/libs/instance.ts @@ -164,11 +164,11 @@ export async function deployAndRegisterMasterInstance( DistributorTypeLib: libraries.distributorTypeLibAddress, NftIdLib: libraries.nftIdLibAddress, PayoutIdLib: libraries.payoutIdLibAddress, + PolicyServiceLib: libraries.policyServiceLibAddress, ReferralLib: libraries.referralLibAddress, RequestIdLib: libraries.requestIdLibAddress, RiskIdLib: libraries.riskIdLibAddress, RoleIdLib: libraries.roleIdLibAddress, - TimestampLib: libraries.timestampLibAddress, UFixedLib: libraries.uFixedLibAddress, } } diff --git a/test/staking/Staking.t.sol b/test/staking/Staking.t.sol index 6ac1dec14..57fec701b 100644 --- a/test/staking/Staking.t.sol +++ b/test/staking/Staking.t.sol @@ -362,7 +362,7 @@ contract StakingTest is GifTest { UFixed rewardRate = stakingReader.getTargetInfo(instanceNftId).rewardRate; ( Amount rewardIncrease, - ) = StakeManagerLib.calculateRewardIncrease( + ) = StakingLib.calculateRewardIncrease( stakingReader, stakeNftId, rewardRate); From 440c9e54af2dfbb7be86f9a0de3fc82583036009 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Thu, 29 Aug 2024 23:22:13 +0000 Subject: [PATCH 08/18] refactor (I)(Service)Authorization --- contracts/authorization/AccessAdmin.sol | 204 +++++++- contracts/authorization/AccessAdminLib.sol | 162 +++++- contracts/authorization/Authorization.sol | 291 ++++------- contracts/authorization/IAccess.sol | 17 +- contracts/authorization/IAccessAdmin.sol | 4 +- contracts/authorization/IAuthorization.sol | 59 +-- .../authorization/IServiceAuthorization.sol | 72 ++- .../authorization/ServiceAuthorization.sol | 282 +++++++++-- .../BasicDistributionAuthorization.sol | 15 +- contracts/distribution/Distribution.sol | 3 +- contracts/instance/InstanceAdmin.sol | 464 ++++++++++-------- .../instance/InstanceAuthorizationV3.sol | 30 +- contracts/instance/InstanceService.sol | 4 +- contracts/oracle/BasicOracleAuthorization.sol | 20 +- contracts/pool/BasicPoolAuthorization.sol | 12 +- .../product/BasicProductAuthorization.sol | 13 +- contracts/product/ClaimService.sol | 6 +- contracts/registry/RegistryAdmin.sol | 172 ++++--- contracts/registry/RegistryAuthorization.sol | 60 ++- contracts/registry/ReleaseAdmin.sol | 154 +++--- contracts/registry/ReleaseRegistry.sol | 8 +- contracts/registry/ServiceAuthorizationV3.sol | 41 +- contracts/shared/ContractLib.sol | 13 +- contracts/shared/IService.sol | 1 + contracts/shared/Service.sol | 5 +- contracts/type/ObjectType.sol | 17 +- contracts/type/RoleId.sol | 113 ++--- contracts/upgradeability/Versionable.sol | 5 +- scripts/libs/libraries.ts | 2 +- scripts/libs/registry.ts | 6 + test/authorization/AccessAdmin.t.sol | 31 +- .../authorization/AccessAdminManageMock.t.sol | 12 +- test/authorization/RegistryAdminEx.sol | 6 +- test/base/Authz.t.sol | 260 ++++++++++ .../DeployAll.t.sol} | 46 +- test/base/GifClusterTest.sol | 2 +- test/base/GifDeployer.sol | 180 ++++++- test/{ => base}/GifDeployer.t.sol | 130 ++--- test/base/GifTest.sol | 238 +++------ .../registration/ComponentTracking.t.sol | 2 +- .../PoolWithReinsuranceAuthorization.sol | 8 + .../ProductWithReinsuranceAuthorization.sol | 8 + .../InstanceAuthorization.t.sol | 25 +- .../authorization/InstanceAuthzRoles.t.sol | 4 +- .../authorization/InstanceAuthzTargets.t.sol | 10 +- test/mock/ServiceAuthorizationMock.sol | 70 +-- test/mock/ServiceMock.sol | 16 +- test/registry/RegistryTestBase.sol | 34 +- .../RegistryServiceHarnessTestBase.sol | 14 +- test/release/ReleaseRegistryConcrete.t.sol | 38 +- test/release/ReleaseRegistryFuzz.t.sol | 23 +- test/staking/Staking.t.sol | 9 +- 52 files changed, 2172 insertions(+), 1249 deletions(-) create mode 100644 test/base/Authz.t.sol rename test/{TestDeployAll.t.sol => base/DeployAll.t.sol} (83%) rename test/{ => base}/GifDeployer.t.sol (71%) rename test/instance/{ => authorization}/InstanceAuthorization.t.sol (57%) diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index f96418e95..0f065216b 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -5,10 +5,13 @@ import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/acce import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import {IAccess} from "./IAccess.sol"; import {IAccessAdmin} from "./IAccessAdmin.sol"; import {IAuthorization} from "./IAuthorization.sol"; import {IRegistry} from "../registry/IRegistry.sol"; +import {IService} from "../shared/IService.sol"; +import {ADMIN_ROLE_NAME, PUBLIC_ROLE_NAME} from "./AccessAdmin.sol"; import {AccessAdminLib} from "./AccessAdminLib.sol"; import {AccessManagerCloneable} from "./AccessManagerCloneable.sol"; import {ContractLib} from "../shared/ContractLib.sol"; @@ -24,6 +27,10 @@ interface IAccessManagedChecker { function authority() external view returns (address); } +function ADMIN_ROLE_NAME() pure returns (string memory) { return "AdminRole"; } +function PUBLIC_ROLE_NAME() pure returns (string memory) { return "PublicRole"; } + + /** * @dev A generic access amin contract that implements role based access control based on OpenZeppelin's AccessManager contract. * The contract provides read functions to query all available roles, targets and access rights. @@ -36,9 +43,6 @@ contract AccessAdmin is { using EnumerableSet for EnumerableSet.AddressSet; - string public constant ADMIN_ROLE_NAME = "AdminRole"; - string public constant PUBLIC_ROLE_NAME = "PublicRole"; - /// @dev admin name used for logging only string internal _adminName; @@ -86,6 +90,8 @@ contract AccessAdmin is /// @dev temporary dynamic functions array bytes4[] private _functions; + // @dev target type specific role id counters + mapping(TargetType => uint64) internal _nextRoleId; modifier onlyDeployer() { // special case for cloned AccessAdmin contracts @@ -226,11 +232,26 @@ contract AccessAdmin is return RoleId.wrap(_authority.PUBLIC_ROLE()); } + function roleExists(Str roleName) public view returns (bool exists) { + // special case admin adn public roles always exist + if (roleName == StrLib.toStr(ADMIN_ROLE_NAME()) || roleName == StrLib.toStr(PUBLIC_ROLE_NAME())) { + return true; + } + + // check if a role id for the name exists + RoleId roleId = _roleForName[roleName].roleId; + if (roleId.eqz()) { + return false; + } + + return _roleInfo[roleId].createdAt.gtz(); + } + function roleExists(RoleId roleId) public view returns (bool exists) { return _roleInfo[roleId].createdAt.gtz(); } - function getRoleForName(string memory name) external view returns (RoleId roleId) { + function getRoleForName(string memory name) public view returns (RoleId roleId) { return _roleForName[StrLib.toStr(name)].roleId; } @@ -338,8 +359,6 @@ contract AccessAdmin is virtual onlyInitializing() { - RoleId adminRoleId = RoleIdLib.toRoleId(_authority.ADMIN_ROLE()); - // setup admin role _createRoleUnchecked( ADMIN_ROLE(), @@ -347,30 +366,76 @@ contract AccessAdmin is adminRoleId: ADMIN_ROLE(), roleType: RoleType.Contract, maxMemberCount: 1, - name: ADMIN_ROLE_NAME})); + name: ADMIN_ROLE_NAME()})); // add this contract as admin role member - _roleMembers[adminRoleId].add(address(this)); + _roleMembers[ + RoleIdLib.toRoleId(_authority.ADMIN_ROLE())].add(address(this)); // setup public role _createRoleUnchecked( PUBLIC_ROLE(), AccessAdminLib.toRole({ adminRoleId: ADMIN_ROLE(), - roleType: RoleType.Gif, + roleType: RoleType.Core, maxMemberCount: type(uint32).max, - name: PUBLIC_ROLE_NAME})); + name: PUBLIC_ROLE_NAME()})); } - function _createTargetWithRole( - address target, - string memory targetName, - RoleId targetRoleId - ) + // TODO cleanup + // function _createTargetWithRole( + // address target, + // string memory targetName, + // RoleId targetRoleId + // ) + // internal + // { + // _createTarget(target, targetName, true, false); + // _grantRoleToAccount(targetRoleId, target); + // } + + // TODO refactor / cleanup +event LogAccessAdminDebugTarget(string name, address target, RoleId roleId, RoleId authorizedRoleId); + + function _authorizeFunctions(IAuthorization authorization, Str target, RoleId roleId) internal { - _createTarget(target, targetName, true, false); - _grantRoleToAccount(targetRoleId, target); + RoleId authorizedRoleId = _toAuthorizedRoleId(authorization, roleId); + address getTargetAddress = getTargetForName(target); +emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, authorizedRoleId); + + _authorizeTargetFunctions( + getTargetForName(target), + authorizedRoleId, + authorization.getAuthorizedFunctions( + target, + roleId), + true); + } + + function _toAuthorizedRoleId(IAuthorization authorization, RoleId roleId) + internal + returns (RoleId authorizedRoleId) + { + // special case for service roles (service roles have predefined role ids) + if (roleId.isServiceRole()) { + + // create service role if missing + if (!roleExists(roleId)) { + _createRole( + roleId, + AccessAdminLib.toRole( + ADMIN_ROLE(), + RoleType.Contract, + 1, + authorization.getRoleName(roleId))); + } + + return roleId; + } + + string memory roleName = authorization.getRoleInfo(roleId).name.toString(); + return authorizedRoleId = getRoleForName(roleName); } function _authorizeTargetFunctions( @@ -570,16 +635,59 @@ contract AccessAdmin is } - function _createTarget( + function _createManagedTarget( address target, string memory targetName, - bool checkAuthority, - bool custom + TargetType targetType ) internal { + _createTarget(target, targetName, targetType, true); + } + + + function _createUncheckedTarget( + address target, + string memory targetName, + TargetType targetType + ) + internal + { + _createTarget(target, targetName, targetType, false); + } + + + function _createTarget( + address target, + string memory targetName, + TargetType targetType, + bool checkAuthority + ) + private + { + // checks AccessAdminLib.checkTargetCreation(this, target, targetName, checkAuthority); - _createTargetUnchecked(target, targetName, custom); + + // effects + _createTargetUnchecked( + target, + targetName, + targetType, + checkAuthority); + + // deal with token handler, if applicable + ( + address tokenHandler, + string memory tokenHandlerName + ) = AccessAdminLib.getTokenHandler(target, targetName, targetType); + + if (tokenHandler != address(0)) { + _createTargetUnchecked( + tokenHandler, + tokenHandlerName, + targetType, + checkAuthority); + } } @@ -605,29 +713,73 @@ contract AccessAdmin is emit LogAccessAdminRoleCreated(_adminName, roleId, info.roleType, info.adminRoleId, info.name.toString()); } - + /// @dev Creates a new target and a corresponding contract role. + /// The function assigns the role to the target and logs the creation. function _createTargetUnchecked( address target, string memory targetName, - bool custom + TargetType targetType, + bool managed ) internal { + // create target role (if not existing) + ( + RoleId targetRoleId, + string memory roleName + ) = _getOrCreateTargetRoleIdAndName(target, targetName, targetType); + + if (!roleExists(targetRoleId)) { + _createRole( + targetRoleId, + AccessAdminLib.toRole(ADMIN_ROLE(), IAccess.RoleType.Contract, 1, roleName)); + } + // create target info Str name = StrLib.toStr(targetName); _targetInfo[target] = TargetInfo({ name: name, - isCustom: custom, + targetType: targetType, + roleId: targetRoleId, createdAt: TimestampLib.blockTimestamp() }); // create name to target mapping _targetForName[name] = target; - // add role to list of roles + // add target to list of targets _targets.push(target); - emit LogAccessAdminTargetCreated(_adminName, target, targetName); + // grant contract role to target + _grantRoleToAccount(targetRoleId, target); + + emit LogAccessAdminTargetCreated(_adminName, targetName, managed, target, targetRoleId); + } + + + function _getOrCreateTargetRoleIdAndName( + address target, + string memory targetName, + TargetType targetType + ) + internal + returns ( + RoleId roleId, + string memory roleName + ) + { + // get roleId + if (targetType == TargetType.Service || targetType == TargetType.GenericService) { + roleId = AccessAdminLib.getServiceRoleId(target, targetType); + } else { + roleId = AccessAdminLib.getTargetRoleId(target, targetType, _nextRoleId[targetType]); + + // increment target type specific role id counter + _nextRoleId[targetType]++; + } + + // create role name + roleName = AccessAdminLib.toRoleName(targetName); } diff --git a/contracts/authorization/AccessAdminLib.sol b/contracts/authorization/AccessAdminLib.sol index 64696789e..cb7b1ec5e 100644 --- a/contracts/authorization/AccessAdminLib.sol +++ b/contracts/authorization/AccessAdminLib.sol @@ -6,19 +6,38 @@ import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/acce import {IAccess} from "./IAccess.sol"; import {IAccessAdmin} from "./IAccessAdmin.sol"; import {IAuthorization} from "./IAuthorization.sol"; +import {IComponent} from "../shared/IComponent.sol"; import {IRegistry} from "../registry/IRegistry.sol"; +import {IService} from "../shared/IService.sol"; import {ContractLib} from "../shared/ContractLib.sol"; import {ObjectType} from "../type/ObjectType.sol"; -import {RoleId} from "../type/RoleId.sol"; +import {RoleId, RoleIdLib} from "../type/RoleId.sol"; import {SelectorLib} from "../type/Selector.sol"; import {Str, StrLib} from "../type/String.sol"; import {TimestampLib} from "../type/Timestamp.sol"; -import {VersionPart} from "../type/Version.sol"; +import {VersionPart, VersionPartLib} from "../type/Version.sol"; library AccessAdminLib { // ACCESS_ADMIN_LIB + string public constant TOKEN_HANDLER_SUFFIX = "Th"; + string public constant ROLE_SUFFIX = "_Role"; + + uint64 public constant SERVICE_DOMAIN_ROLE_FACTOR = 100; + uint64 public constant COMPONENT_ROLE_FACTOR = 1000; + uint64 public constant COMPONENT_ROLE_MAX = 19000; + + uint64 public constant CORE_ROLE_MIN = 100; + uint64 public constant SERVICE_ROLE_MIN = 1000; // + service domain * SERVICE_ROLE_FACTOR + release + uint64 public constant SERVICE_ROLE_FACTOR = 1000; + uint64 public constant INSTANCE_ROLE_MIN = 100000; + + // MUST match with Authorization.COMPONENT_ROLE_MIN + uint64 public constant COMPONENT_ROLE_MIN = 110000; + + uint64 public constant CUSTOM_ROLE_MIN = 1000000; + function getSelectors( IAccess.FunctionInfo[] memory functions @@ -36,6 +55,7 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB } } + function checkRoleCreation( IAccessAdmin accessAdmin, RoleId roleId, @@ -71,6 +91,7 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB } } + function checkTargetCreation( IAccessAdmin accessAdmin, address target, @@ -184,6 +205,143 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB } } + + function getServiceRoleId( + address serviceAddress, + IAccess.TargetType serviceTargetType + ) + public + view + returns (RoleId serviceRoleId) + { + IService service = IService(serviceAddress); + + if (serviceTargetType == IAccess.TargetType.Service) { + return RoleIdLib.toServiceRoleId(service.getDomain(), service.getRelease()); + } else if (serviceTargetType == IAccess.TargetType.GenericService) { + return RoleIdLib.toGenericServiceRoleId(service.getDomain()); + } + + revert IAccessAdmin.ErrorAccessAdminInvalidServiceType(serviceAddress, serviceTargetType); + } + + + function getVersionedServiceRoleId( + ObjectType serviceDomain, + VersionPart release + ) + public + pure + returns (RoleId serviceRoleId) + { + return RoleIdLib.toRoleId( + SERVICE_ROLE_MIN + SERVICE_ROLE_FACTOR * serviceDomain.toInt() + release.toInt()); + } + + + function getGenericServiceRoleId( + ObjectType serviceDomain + ) + public + pure + returns (RoleId serviceRoleId) + { + return RoleIdLib.toRoleId( + SERVICE_ROLE_MIN + SERVICE_ROLE_FACTOR * serviceDomain.toInt() + VersionPartLib.releaseMax().toInt()); + } + + + function getCustomRoleId(uint64 index) + public + view + returns (RoleId customRoleId) + { + return RoleIdLib.toRoleId(CUSTOM_ROLE_MIN + index); + } + + + function isCustomRole(RoleId roleId) + public + pure + returns (bool) + { + return roleId.toInt() >= CUSTOM_ROLE_MIN; + } + + + function getTargetRoleId( + address target, + IAccess.TargetType targetType, + uint64 index + ) + public + view + returns (RoleId targetRoleId) + { + if (targetType == IAccess.TargetType.Core) { + return RoleIdLib.toRoleId(CORE_ROLE_MIN + index); + } + + if (targetType == IAccess.TargetType.Service || targetType == IAccess.TargetType.GenericService ) { + return getServiceRoleId(target, targetType); + } + + if (targetType == IAccess.TargetType.Instance) { + return RoleIdLib.toRoleId(INSTANCE_ROLE_MIN + index); + } + + if (targetType == IAccess.TargetType.Component) { + return RoleIdLib.toRoleId(COMPONENT_ROLE_MIN + index); + } + + if (targetType == IAccess.TargetType.Custom) { + return RoleIdLib.toRoleId(CUSTOM_ROLE_MIN + index); + } + + revert IAccessAdmin.ErrorAccessAdminInvalidTargetType(target, targetType); + } + + + function getTokenHandler( + address target, + string memory targetName, + IAccess.TargetType targetType + ) + public + view + returns ( + address tokenHandler, + string memory tokenHandlerName + ) + { + // not component or core (we need to check core because of staking) + if (targetType != IAccess.TargetType.Component && targetType != IAccess.TargetType.Core) { + return (address(0), ""); + } + + // not contract + if (!ContractLib.isContract(target)) { + return (address(0), ""); + } + + // not component + if (!ContractLib.supportsInterface(target, type(IComponent).interfaceId)) { + return (address(0), ""); + } + + tokenHandler = address(IComponent(target).getTokenHandler()); + tokenHandlerName = string(abi.encodePacked(targetName, TOKEN_HANDLER_SUFFIX)); + } + + + function toRoleName(string memory name) public view returns (string memory) { + return string( + abi.encodePacked( + name, + ROLE_SUFFIX)); + } + + function toRole( RoleId adminRoleId, IAccessAdmin.RoleType roleType, diff --git a/contracts/authorization/Authorization.sol b/contracts/authorization/Authorization.sol index a89e6b225..8236a0c03 100644 --- a/contracts/authorization/Authorization.sol +++ b/contracts/authorization/Authorization.sol @@ -4,75 +4,51 @@ pragma solidity ^0.8.20; import {IAccess} from "./IAccess.sol"; import {IAuthorization} from "./IAuthorization.sol"; -import {InitializableERC165} from "../shared/InitializableERC165.sol"; +import {ADMIN_ROLE_NAME, PUBLIC_ROLE_NAME} from "./AccessAdmin.sol"; import {ObjectType, ObjectTypeLib} from "../type/ObjectType.sol"; -import {RoleId, RoleIdLib, ADMIN_ROLE} from "../type/RoleId.sol"; +import {RoleId, RoleIdLib, ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; import {SelectorLib} from "../type/Selector.sol"; +import {ServiceAuthorization} from "../authorization/ServiceAuthorization.sol"; import {Str, StrLib} from "../type/String.sol"; import {TimestampLib} from "../type/Timestamp.sol"; import {VersionPart, VersionPartLib} from "../type/Version.sol"; + contract Authorization is - InitializableERC165, + ServiceAuthorization, IAuthorization { - uint256 public constant GIF_RELEASE = 3; - string public constant ROLE_NAME_SUFFIX = "Role"; - string public constant SERVICE_ROLE_NAME_SUFFIX = "ServiceRole"; + // MUST match with AccessAdminLib.COMPONENT_ROLE_MIN + uint64 public constant COMPONENT_ROLE_MIN = 110000; uint64 internal _nextGifContractRoleId; - ObjectType[] internal _serviceDomains; - mapping(ObjectType domain => Str target) internal _serviceTarget; + // mapping(ObjectType domain => Str target) internal _serviceTarget; - string internal _mainTargetName = "Component"; - string internal _tokenHandlerName = "ComponentTH"; + string internal _tokenHandlerName = "ComponentTh"; - ObjectType internal _domain; - Str internal _mainTarget; Str internal _tokenHandlerTarget; - Str[] internal _targets; - - mapping(Str target => RoleId roleid) internal _targetRole; - mapping(Str target => bool exists) internal _targetExists; - - RoleId[] internal _roles; - mapping(RoleId role => RoleInfo info) internal _roleInfo; - - mapping(Str target => RoleId[] authorizedRoles) internal _authorizedRoles; - mapping(Str target => mapping(RoleId authorizedRole => IAccess.FunctionInfo[] functions)) internal _authorizedFunctions; constructor( string memory mainTargetName, - ObjectType targetDomain, + ObjectType domain, + uint8 release, + string memory commitHash, bool isComponent, bool includeTokenHandler ) + ServiceAuthorization(mainTargetName, domain, release, commitHash) { - // checks - if (bytes(mainTargetName).length == 0) { - revert ErrorAuthorizationMainTargetNameEmpty(); - } - - if (targetDomain == ObjectTypeLib.zero()) { - revert ErrorAuthorizationTargetDomainZero(); - } - - // effects - _initializeERC165(); - - _domain = targetDomain; - _mainTargetName = mainTargetName; - _mainTarget = StrLib.toStr(mainTargetName); _nextGifContractRoleId = 10; + // setup main target if (isComponent) { - if (targetDomain.eqz()) { + if (domain.eqz()) { revert ErrorAuthorizationTargetDomainZero(); } - RoleId mainRoleId = RoleIdLib.toComponentRoleId(targetDomain, 0); + RoleId mainRoleId = RoleIdLib.toRoleId(COMPONENT_ROLE_MIN); string memory mainRolName = _toTargetRoleName(_mainTargetName); _addTargetWithRole( @@ -80,8 +56,9 @@ contract Authorization is mainRoleId, mainRolName); } else { - _addGifContractTarget(_mainTargetName); + _addGifTarget(_mainTargetName); } + // setup use case specific parts _setupServiceTargets(); _setupRoles(); // not including main target role @@ -90,7 +67,7 @@ contract Authorization is // setup component token handler if (includeTokenHandler) { - _tokenHandlerName = string(abi.encodePacked(mainTargetName, "TH")); + _tokenHandlerName = string(abi.encodePacked(mainTargetName, "Th")); _tokenHandlerTarget = StrLib.toStr(_tokenHandlerName); _addTarget(_tokenHandlerName); _setupTokenHandlerAuthorizations(); @@ -99,47 +76,12 @@ contract Authorization is _registerInterfaceNotInitializing(type(IAuthorization).interfaceId); } - function getDomain() external view returns(ObjectType targetDomain) { - return _domain; - } - - function getServiceDomains() external view returns(ObjectType[] memory serviceDomains) { - return _serviceDomains; - } - - function getComponentRole(ObjectType componentDomain) public pure returns(RoleId roleId) { - return RoleIdLib.toComponentRoleId(componentDomain, 0); - } - - function getServiceRole(ObjectType serviceDomain) public virtual pure returns (RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion( - serviceDomain, - getRelease()); - } - - function getServiceTarget(ObjectType serviceDomain) external view returns(Str serviceTarget) { - return _serviceTarget[serviceDomain]; - } - - function getRoles() external view returns(RoleId[] memory roles) { - return _roles; - } - - function roleExists(RoleId roleId) public view returns(bool exists) { - return _roleInfo[roleId].roleType != RoleType.Undefined; - } - - function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory info) { - return _roleInfo[roleId]; - } - function getMainTargetName() public virtual view returns (string memory name) { - return _mainTargetName; - } + // TODO cleanup + // function roleExists(RoleId roleId) public view returns(bool exists) { + // return _roleInfo[roleId].roleType != RoleType.Undefined; + // } - function getMainTarget() public view returns(Str) { - return _mainTarget; - } function getTokenHandlerName() public view returns(string memory) { return _tokenHandlerName; @@ -161,21 +103,18 @@ contract Authorization is return target == _mainTarget || _targetExists[target]; } - function getTargetRole(Str target) public view returns(RoleId roleId) { - return _targetRole[target]; - } - - function getAuthorizedRoles(Str target) external view returns(RoleId[] memory roleIds) { - return _authorizedRoles[target]; - } + // TODO cleanup + // function getTargetRole(Str target) public view returns(RoleId roleId) { + // return _targetRole[target]; + // } - function getAuthorizedFunctions(Str target, RoleId roleId) external view returns(IAccess.FunctionInfo[] memory authorizatedFunctions) { - return _authorizedFunctions[target][roleId]; - } + // function getAuthorizedRoles(Str target) external view returns(RoleId[] memory roleIds) { + // return _authorizedRoles[target]; + // } - function getRelease() public virtual pure returns(VersionPart release) { - return VersionPartLib.toVersionPart(GIF_RELEASE); - } + // function getAuthorizedFunctions(Str target, RoleId roleId) external view returns(IAccess.FunctionInfo[] memory authorizatedFunctions) { + // return _authorizedFunctions[target][roleId]; + // } /// @dev Sets up the relevant service targets for the component. /// Overwrite this function for use case specific authorizations. @@ -202,7 +141,7 @@ contract Authorization is // solhint-disable-next-line no-empty-blocks function _setupTargetAuthorizations() internal virtual {} - function _addGifContractTarget(string memory contractName) internal { + function _addGifTarget(string memory contractName) internal { RoleId contractRoleId = RoleIdLib.toRoleId(_nextGifContractRoleId++); string memory contractRoleName = string( @@ -216,60 +155,50 @@ contract Authorization is contractRoleName); } - /// @dev Add the service target role for the specified service domain - function _addServiceTargetWithRole(ObjectType serviceDomain) internal { - // add service domain - _serviceDomains.push(serviceDomain); + // TODO cleanup + // /// @dev Add the service target role for the specified service domain + // function _addServiceTargetWithRole(ObjectType serviceDomain) internal { + // // add service domain + // _serviceDomains.push(serviceDomain); - // get versioned target name - string memory serviceTargetName = ObjectTypeLib.toVersionedName( - ObjectTypeLib.toName(serviceDomain), - "Service", - getRelease()); + // // get versioned target name + // string memory serviceTargetName = ObjectTypeLib.toVersionedName( + // ObjectTypeLib.toName(serviceDomain), + // "Service", + // getRelease()); - _serviceTarget[serviceDomain] = StrLib.toStr(serviceTargetName); + // // _serviceTarget[serviceDomain] = StrLib.toStr(serviceTargetName); - RoleId serviceRoleId = getServiceRole(serviceDomain); - string memory serviceRoleName = ObjectTypeLib.toVersionedName( - ObjectTypeLib.toName(serviceDomain), - "ServiceRole", - getRelease()); + // RoleId serviceRoleId = getServiceRole(serviceDomain); + // string memory serviceRoleName = ObjectTypeLib.toVersionedName( + // ObjectTypeLib.toName(serviceDomain), + // "ServiceRole", + // getRelease()); - _addTargetWithRole( - serviceTargetName, - serviceRoleId, - serviceRoleName); - } - - - /// @dev Use this method to to add an authorized role. - function _addRole(RoleId roleId, RoleInfo memory info) internal { - _roles.push(roleId); - _roleInfo[roleId] = info; - } + // _addTargetWithRole( + // serviceTargetName, + // serviceRoleId, + // serviceRoleName); + // } - /// @dev Add a contract role for the provided role id and name. - function _addContractRole(RoleId roleId, string memory name) internal { - _addRole( - roleId, - _toRoleInfo( - ADMIN_ROLE(), - RoleType.Contract, - 1, - name)); - } + // /// @dev Use this method to to add an authorized role. + // function _addRole(RoleId roleId, RoleInfo memory info) internal { + // _roles.push(roleId); + // _roleInfo[roleId] = info; + // } - /// @dev Add the versioned service role for the specified service domain - function _addServiceRole(ObjectType serviceDomain) internal { - _addContractRole( - getServiceRole(serviceDomain), - ObjectTypeLib.toVersionedName( - ObjectTypeLib.toName(serviceDomain), - SERVICE_ROLE_NAME_SUFFIX, - getRelease())); - } + // /// @dev Add a contract role for the provided role id and name. + // function _addContractRole(RoleId roleId, string memory name) internal { + // _addRole( + // roleId, + // _toRoleInfo( + // ADMIN_ROLE(), + // RoleType.Contract, + // 1, + // name)); + // } /// @dev Add a contract role for the provided role id and name. @@ -284,58 +213,20 @@ contract Authorization is } - /// @dev Use this method to to add an authorized target together with its target role. - function _addTargetWithRole( - string memory targetName, - RoleId roleId, - string memory roleName - ) - internal - { - // add target - Str target = StrLib.toStr(targetName); - _targets.push(target); - - _targetExists[target] = true; - - // link role to target if defined - if (roleId != RoleIdLib.zero()) { - // add role if new - if (!roleExists(roleId)) { - _addContractRole(roleId, roleName); - } - - // link target to role - _targetRole[target] = roleId; - } - } - - /// @dev Use this method to to add an authorized target. function _addTarget(string memory name) internal { _addTargetWithRole(name, RoleIdLib.zero(), ""); } - - /// @dev Use this method to authorize the specified role to access the target. - function _authorizeForTarget(string memory target, RoleId authorizedRoleId) - internal - returns (IAccess.FunctionInfo[] storage authorizatedFunctions) - { - Str targetStr = StrLib.toStr(target); - _authorizedRoles[targetStr].push(authorizedRoleId); - return _authorizedFunctions[targetStr][authorizedRoleId]; - } - - - /// @dev Use this method to authorize a specific function authorization - function _authorize(IAccess.FunctionInfo[] storage functions, bytes4 selector, string memory name) internal { - functions.push( - IAccess.FunctionInfo({ - selector: SelectorLib.toSelector(selector), - name: StrLib.toStr(name), - createdAt: TimestampLib.blockTimestamp()})); - } + // TODO cleanup + // /// @dev Use this method to authorize a specific function authorization + // function _authorize(IAccess.FunctionInfo[] storage functions, bytes4 selector, string memory name) internal { + // functions.push( + // IAccess.FunctionInfo({ + // selector: SelectorLib.toSelector(selector), + // name: StrLib.toStr(name), + // createdAt: TimestampLib.blockTimestamp()})); + // } /// @dev role id for targets registry, staking and instance @@ -344,7 +235,7 @@ contract Authorization is pure returns (RoleId targetRoleId) { - return RoleIdLib.roleForType(targetDomain); + return RoleIdLib.toRoleId(100 * targetDomain.toInt()); } @@ -355,16 +246,16 @@ contract Authorization is ROLE_NAME_SUFFIX)); } - - /// @dev creates a role info object from the provided parameters - function _toRoleInfo(RoleId adminRoleId, RoleType roleType, uint32 maxMemberCount, string memory name) internal view returns (RoleInfo memory info) { - return RoleInfo({ - name: StrLib.toStr(name), - adminRoleId: adminRoleId, - roleType: roleType, - maxMemberCount: maxMemberCount, - createdAt: TimestampLib.blockTimestamp(), - pausedAt: TimestampLib.max()}); - } + // TODO cleanup + // /// @dev creates a role info object from the provided parameters + // function _toRoleInfo(RoleId adminRoleId, RoleType roleType, uint32 maxMemberCount, string memory name) internal view returns (RoleInfo memory info) { + // return RoleInfo({ + // name: StrLib.toStr(name), + // adminRoleId: adminRoleId, + // roleType: roleType, + // maxMemberCount: maxMemberCount, + // createdAt: TimestampLib.blockTimestamp(), + // pausedAt: TimestampLib.max()}); + // } } diff --git a/contracts/authorization/IAccess.sol b/contracts/authorization/IAccess.sol index 3dff111a1..c6a7b421d 100644 --- a/contracts/authorization/IAccess.sol +++ b/contracts/authorization/IAccess.sol @@ -10,23 +10,34 @@ interface IAccess { enum RoleType { Undefined, // no role must have this type + Core, // GIF core roles Contract, // roles assigned to contracts, cannot be revoked - Gif, // framework roles that may be freely assigned and revoked + Custom // use case specific rules for components + } + + enum TargetType { + Undefined, // no target must have this type + Core, // GIF core contracts + GenericService, // release independent service contracts + Service, // service contracts + Instance, // instance contracts + Component, // instance contracts Custom // use case specific rules for components } struct RoleInfo { + Str name; RoleId adminRoleId; RoleType roleType; uint32 maxMemberCount; - Str name; Timestamp createdAt; Timestamp pausedAt; } struct TargetInfo { Str name; - bool isCustom; + TargetType targetType; + RoleId roleId; Timestamp createdAt; } diff --git a/contracts/authorization/IAccessAdmin.sol b/contracts/authorization/IAccessAdmin.sol index 622ab642c..33c03b65d 100644 --- a/contracts/authorization/IAccessAdmin.sol +++ b/contracts/authorization/IAccessAdmin.sol @@ -24,7 +24,7 @@ interface IAccessAdmin is // roles, targets and functions event LogAccessAdminRoleCreated(string admin, RoleId roleId, RoleType roleType, RoleId roleAdminId, string name); - event LogAccessAdminTargetCreated(string admin, address target, string name); + event LogAccessAdminTargetCreated(string admin, string name, bool managed, address target, RoleId roleId); event LogAccessAdminRoleGranted(string admin, address account, string roleName); event LogAccessAdminRoleRevoked(string admin, address account, string roleName); @@ -49,6 +49,8 @@ interface IAccessAdmin is error ErrorAccessAdminAccessManagerEmptyName(); // check target + error ErrorAccessAdminInvalidTargetType(address target, TargetType targetType); + error ErrorAccessAdminInvalidServiceType(address target, TargetType serviceTargetType); error ErrorAccessAdminTargetNotCreated(address target); error ErrorAccessAdminTargetNotRegistered(address target); error ErrorAccessAdminTargetTypeMismatch(address target, ObjectType expectedType, ObjectType actualType); diff --git a/contracts/authorization/IAuthorization.sol b/contracts/authorization/IAuthorization.sol index 4551f18b7..5f77b2080 100644 --- a/contracts/authorization/IAuthorization.sol +++ b/contracts/authorization/IAuthorization.sol @@ -1,54 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import {IAccess} from "../authorization/IAccess.sol"; -import {ObjectType} from "../type/ObjectType.sol"; +import {IServiceAuthorization} from "./IServiceAuthorization.sol"; import {RoleId} from "../type/RoleId.sol"; import {Str} from "../type/String.sol"; -import {VersionPart} from "../type/Version.sol"; + interface IAuthorization is - IERC165, - IAccess + IServiceAuthorization { - error ErrorAuthorizationMainTargetNameEmpty(); - error ErrorAuthorizationTargetDomainZero(); - - /// @dev Returns the main domain of the authorization. - function getDomain() external view returns(ObjectType targetDomain); - - /// @dev Returns the list of service targets. - function getServiceDomains() external view returns(ObjectType[] memory serviceDomains); - - /// @dev Returns the service role for the specified service domain. - function getServiceRole(ObjectType serviceDomain) external pure returns (RoleId serviceRoleId); - - /// @dev Returns the service target for the specified domain. - function getServiceTarget(ObjectType serviceDomain) external view returns(Str serviceTarget); - - /// @dev Returns the component role for the specified domain. - function getComponentRole(ObjectType componentDomain) external view returns(RoleId componentRoleId); - - /// @dev Returns the list of involved roles. - function getRoles() external view returns(RoleId[] memory roles); - - /// @dev Returns the name for the provided role id. - function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory roleInfo); - - /// @dev Returns true iff the specified role id exists. - function roleExists(RoleId roleId) external view returns(bool exists); - - /// @dev Returns the main target id name as string. - /// This name is used to derive the target id and a corresponding target role name - /// Overwrite this function to change the basic pool target name. - function getMainTargetName() external view returns (string memory name); - - /// @dev Returns the main target. - function getMainTarget() external view returns(Str target); - /// @dev Returns the token hander name. /// Only components have a token handler. function getTokenHandlerName() external view returns(string memory name); @@ -62,19 +23,5 @@ interface IAuthorization is /// @dev Returns true iff the specified target exists. function targetExists(Str target) external view returns(bool exists); - - /// @dev Returns the role id associated with the target. - /// If no role is associated with the target the zero role id is returned. - function getTargetRole(Str target) external view returns(RoleId roleId); - - /// @dev For the given target the list of authorized role ids is returned - function getAuthorizedRoles(Str target) external view returns(RoleId[] memory roleIds); - - /// @dev For the given target and role id the list of authorized functions is returned - function getAuthorizedFunctions(Str target, RoleId roleId) external view returns(FunctionInfo[] memory authorizatedFunctions); - - /// @dev Returns the release (VersionPart) for which the authorizations are defined by this contract. - /// Matches with the release returned by the linked service authorization. - function getRelease() external view returns(VersionPart release); } diff --git a/contracts/authorization/IServiceAuthorization.sol b/contracts/authorization/IServiceAuthorization.sol index 1b0e14d65..5549fa508 100644 --- a/contracts/authorization/IServiceAuthorization.sol +++ b/contracts/authorization/IServiceAuthorization.sol @@ -5,36 +5,74 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IAccess} from "../authorization/IAccess.sol"; import {ObjectType} from "../type/ObjectType.sol"; +import {RoleId} from "../type/RoleId.sol"; +import {Str} from "../type/String.sol"; import {VersionPart} from "../type/Version.sol"; -interface IServiceAuthorization is IERC165 { +interface IServiceAuthorization is + IERC165, + IAccess +{ - /// @dev Returns the commit hash representing the deployed release - function getCommitHash() - external - view - returns(string memory commitHash); + error ErrorAuthorizationMainTargetNameEmpty(); + error ErrorAuthorizationTargetDomainZero(); + error ErrorAuthorizationReleaseInvalid(uint8 release); + error ErrorAuthorizationCommitHashInvalid(string commitHash); - /// @dev Returns the release (VersionPart) for which the service authorizations are defined by this contract. - function getRelease() - external - view - returns(VersionPart release); + /// @dev Returns the main domain of the authorization. + function getDomain() external view returns(ObjectType targetDomain); - /// @dev Returns the service domain for the provided index. - function getServiceDomain(uint256 idx) external view returns(ObjectType serviceDomain); + /// @dev Returns the release (VersionPart) for which the authorizations are defined by this contract. + /// Matches with the release returned by the linked service authorization. + function getRelease() external view returns(VersionPart release); + + /// @dev Returns the commit hash for the related GIF release. + function getCommitHash() external view returns(string memory commitHash); + + /// @dev Returns the main target id name as string. + /// This name is used to derive the target id and a corresponding target role name + /// Overwrite this function to change the basic pool target name. + function getMainTargetName() external view returns (string memory name); + + /// @dev Returns the main target. + function getMainTarget() external view returns(Str target); /// @dev Returns the full list of service domains for this release. /// Services need to be registered for the release in revers order of this list. function getServiceDomains() external view returns(ObjectType[] memory serviceDomains); + /// @dev Returns the service domain for the provided index. + function getServiceDomain(uint256 idx) external view returns(ObjectType serviceDomain); + + /// @dev Returns the service target for the specified domain. + function getServiceTarget(ObjectType serviceDomain) external view returns(Str serviceTarget); + + /// @dev Returns the service target for the specified domain. + function getServiceRole(ObjectType serviceDomain) external view returns(RoleId serviceRoleId); + /// @dev Returns the expected service address for the provided domain. function getServiceAddress(ObjectType serviceDomain) external view returns(address service); - /// @dev Given the service domain this function returns the list of other service domains that are authorized to access this service. - function getAuthorizedDomains(ObjectType serviceDomain) external view returns(ObjectType[] memory authorizatedDomains); + /// @dev Returns the role id associated with the target. + /// If no role is associated with the target the zero role id is returned. + function getTargetRole(Str target) external view returns(RoleId roleId); + + /// @dev Returns true iff the role exists. + function roleExists(RoleId roleId) external view returns(bool exists); + + /// @dev Returns the list of involved roles. + function getRoles() external view returns(RoleId[] memory roles); + + /// @dev Returns the role info for the provided role id. + function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory roleInfo); + + /// @dev Returns the name for the provided role id. + function getRoleName(RoleId roleId) external view returns (string memory roleName); + + /// @dev For the given target the list of authorized role ids is returned + function getAuthorizedRoles(Str target) external view returns(RoleId[] memory roleIds); - /// @dev For the given service domain and authorized domain the function returns the list of authorized functions - function getAuthorizedFunctions(ObjectType serviceDomain, ObjectType authorizedDomain) external view returns(IAccess.FunctionInfo[] memory authorizatedFunctions); + /// @dev For the given target and role id the list of authorized functions is returned + function getAuthorizedFunctions(Str target, RoleId roleId) external view returns(FunctionInfo[] memory authorizatedFunctions); } diff --git a/contracts/authorization/ServiceAuthorization.sol b/contracts/authorization/ServiceAuthorization.sol index c460d03e8..c33ebc1e0 100644 --- a/contracts/authorization/ServiceAuthorization.sol +++ b/contracts/authorization/ServiceAuthorization.sol @@ -1,77 +1,206 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - import {IAccess} from "../authorization/IAccess.sol"; -import {ObjectType} from "../type/ObjectType.sol"; import {IServiceAuthorization} from "./IServiceAuthorization.sol"; + +import {ADMIN_ROLE_NAME, PUBLIC_ROLE_NAME} from "./AccessAdmin.sol"; +import {InitializableERC165} from "../shared/InitializableERC165.sol"; +import {ObjectType, ObjectTypeLib, ALL} from "../type/ObjectType.sol"; +import {RoleId, RoleIdLib, ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; import {SelectorLib} from "../type/Selector.sol"; -import {StrLib} from "../type/String.sol"; +import {Str, StrLib} from "../type/String.sol"; import {TimestampLib} from "../type/Timestamp.sol"; import {VersionPart, VersionPartLib} from "../type/Version.sol"; -/// @dev Base contract for release specific service authorization contracts. + +/// @dev Base contract for release specific service authorization contracts and for Authorization contracts. contract ServiceAuthorization is + InitializableERC165, IServiceAuthorization { + + string public constant COMMIT_HASH = "1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a"; + uint256 public constant COMMIT_HASH_LENGTH = 40; uint256 public constant GIF_INITIAL_VERSION = 3; - uint256 public immutable VERSION; - string public COMMIT_HASH; + string public constant SERVICE_NAME_SUFFIX = "Service"; + string public constant ROLE_NAME_SUFFIX = "_Role"; + ObjectType public immutable DOMAIN; + uint256 internal immutable _release; + string internal _commitHash; + + string internal _mainTargetName; + Str internal _mainTarget; + + // services ObjectType[] internal _serviceDomains; mapping(ObjectType domain => address service) internal _serviceAddress; - mapping(ObjectType domain => ObjectType[] authorizedDomains) internal _authorizedDomains; - mapping(ObjectType domain => mapping(ObjectType authorizedDomain => IAccess.FunctionInfo[] functions)) internal _authorizedFunctions; - constructor(string memory commitHash, uint256 version) { - assert(bytes(commitHash).length == COMMIT_HASH_LENGTH); - assert(version >= GIF_INITIAL_VERSION); + // roles + RoleId[] internal _roles; + mapping(RoleId role => RoleInfo info) internal _roleInfo; + + // targets + Str[] internal _targets; + mapping(Str target => bool exists) internal _targetExists; + mapping(Str target => RoleId roleId) internal _targetRole; + mapping(Str target => RoleId[] authorizedRoles) internal _authorizedRoles; + mapping(Str target => mapping(RoleId authorizedRole => IAccess.FunctionInfo[] functions)) internal _authorizedFunctions; + - COMMIT_HASH = commitHash; - VERSION = version; + constructor( + string memory mainTargetName, + ObjectType domain, + uint8 release, + string memory commitHash + ) + { + // checks + if (bytes(mainTargetName).length == 0) { + revert ErrorAuthorizationMainTargetNameEmpty(); + } + + if (domain == ObjectTypeLib.zero()) { + revert ErrorAuthorizationTargetDomainZero(); + } + + if (release < VersionPartLib.releaseMin().toInt() || release >= VersionPartLib.releaseMax().toInt()) { + revert ErrorAuthorizationReleaseInvalid(release); + } + + if (bytes(commitHash).length != COMMIT_HASH_LENGTH) { + revert ErrorAuthorizationCommitHashInvalid(commitHash); + } + + // effects + _initializeERC165(); + _registerInterfaceNotInitializing(type(IServiceAuthorization).interfaceId); + + _mainTargetName = mainTargetName; + _mainTarget = StrLib.toStr(mainTargetName); + + DOMAIN = domain; + _release = release; + _commitHash = commitHash; + + _setupAdminAndPublicRoles(); _setupDomains(); _setupDomainAuthorizations(); } + /// @inheritdoc IServiceAuthorization + function getDomain() public view returns(ObjectType targetDomain) { + return DOMAIN; + } + + /// @inheritdoc IServiceAuthorization + function getRelease() public view returns(VersionPart release) { + return VersionPartLib.toVersionPart(_release); + } + + /// @inheritdoc IServiceAuthorization function getCommitHash() external view returns(string memory commitHash) { - return COMMIT_HASH; + return _commitHash; + } + + /// @inheritdoc IServiceAuthorization + function getMainTargetName() public view returns (string memory name) { + return _mainTargetName; } - function getRelease() external view returns(VersionPart release) { - return VersionPartLib.toVersionPart(VERSION); + /// @inheritdoc IServiceAuthorization + function getMainTarget() external view returns(Str target) { + return _mainTarget; } + /// @inheritdoc IServiceAuthorization function getServiceDomains() external view returns(ObjectType[] memory serviceDomains) { return _serviceDomains; } + /// @inheritdoc IServiceAuthorization function getServiceDomain(uint256 idx) external view returns(ObjectType serviceDomain) { return _serviceDomains[idx]; } + /// @inheritdoc IServiceAuthorization + function getServiceTarget(ObjectType serviceDomain) public view returns(Str target) { + string memory serviceTargetName = ObjectTypeLib.toVersionedName( + ObjectTypeLib.toName(serviceDomain), + "Service", + getRelease()); + + return StrLib.toStr(serviceTargetName); + } + + /// @inheritdoc IServiceAuthorization + function getServiceRole(ObjectType serviceDomain) public view returns(RoleId serviceRoleId) { + // special case domain ALL + if (serviceDomain == ALL()) { + return PUBLIC_ROLE(); + } + + Str target = getServiceTarget(serviceDomain); + return getTargetRole(target); + } + + /// @inheritdoc IServiceAuthorization function getServiceAddress(ObjectType serviceDomain) external view returns(address service) { return _serviceAddress[serviceDomain]; } - function getAuthorizedDomains(ObjectType serviceDomain) external view returns(ObjectType[] memory authorizatedDomains) { - return _authorizedDomains[serviceDomain]; + /// @inheritdoc IServiceAuthorization + function getTargetRole(Str target) public view returns(RoleId roleId) { + return _targetRole[target]; + } + + /// @inheritdoc IServiceAuthorization + function roleExists(RoleId roleId) public view returns(bool exists) { + return _roleInfo[roleId].roleType != RoleType.Undefined; + } + + /// @inheritdoc IServiceAuthorization + function getRoles() external view returns(RoleId[] memory roles) { + return _roles; + } + + /// @inheritdoc IServiceAuthorization + function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory info) { + return _roleInfo[roleId]; } - function getAuthorizedFunctions(ObjectType serviceDomain, ObjectType authorizedDomain) external view returns(IAccess.FunctionInfo[] memory authorizatedFunctions) { - return _authorizedFunctions[serviceDomain][authorizedDomain]; + /// @inheritdoc IServiceAuthorization + function getRoleName(RoleId roleId) external view returns (string memory roleName) { + return _roleInfo[roleId].name.toString(); } - // ERC165 - function supportsInterface(bytes4 interfaceId) public pure returns (bool) { - return ( - interfaceId == type(IServiceAuthorization).interfaceId || - interfaceId == type(IERC165).interfaceId - ); + /// @inheritdoc IServiceAuthorization + function getAuthorizedRoles(Str target) external view returns(RoleId[] memory roleIds) { + return _authorizedRoles[target]; } + /// @inheritdoc IServiceAuthorization + function getAuthorizedFunctions(Str target, RoleId roleId) external view returns(FunctionInfo[] memory authorizatedFunctions) { + return _authorizedFunctions[target][roleId]; + } + + // TODO cleanup + // // ERC165 + // function supportsInterface(bytes4 interfaceId) public pure returns (bool) { + // return ( + // interfaceId == type(IServiceAuthorization).interfaceId || + // interfaceId == type(IERC165).interfaceId + // ); + // } + + + function _setupAdminAndPublicRoles() internal { + _addRole(ADMIN_ROLE(), _toRoleInfo(ADMIN_ROLE(), RoleType.Core, 1, ADMIN_ROLE_NAME())); + _addRole(PUBLIC_ROLE(), _toRoleInfo(ADMIN_ROLE(), RoleType.Core, 1, PUBLIC_ROLE_NAME())); + } /// @dev Overwrite this function for a specific realease. // solhint-disable-next-line no-empty-blocks @@ -83,20 +212,111 @@ contract ServiceAuthorization is /// @dev Use this method to to add an authorized domain. /// The services will need to be registered in the order they are added using this function. - function _authorizeDomain(ObjectType serviceDomain, address serviceAddress) internal { + function _authorizeServiceDomain(ObjectType serviceDomain, address serviceAddress) internal { _serviceDomains.push(serviceDomain); _serviceAddress[serviceDomain] = serviceAddress; + + string memory serviceName = ObjectTypeLib.toVersionedName( + ObjectTypeLib.toName(serviceDomain), + SERVICE_NAME_SUFFIX, + getRelease()); + + _addTargetWithRole( + serviceName, + RoleIdLib.toServiceRoleId(serviceDomain, getRelease()), + string(abi.encodePacked(serviceName, ROLE_NAME_SUFFIX))); + } + + + /// @dev Use this method to to add an authorized target together with its target role. + function _addTargetWithRole( + string memory targetName, + RoleId roleId, + string memory roleName + ) + internal + { + // add target + Str target = StrLib.toStr(targetName); + _targets.push(target); + + _targetExists[target] = true; + + // link role to target if defined + if (roleId != RoleIdLib.zero()) { + // add role if new + if (!roleExists(roleId)) { + _addContractRole(roleId, roleName); + } + + // link target to role + _targetRole[target] = roleId; + } + } + + + /// @dev Add a contract role for the provided role id and name. + function _addContractRole(RoleId roleId, string memory name) internal { + _addRole( + roleId, + _toRoleInfo( + ADMIN_ROLE(), + RoleType.Contract, + 1, + name)); } + + /// @dev Use this method to to add an authorized role. + function _addRole(RoleId roleId, RoleInfo memory info) internal { + _roles.push(roleId); + _roleInfo[roleId] = info; + } + + + /// @dev creates a role info object from the provided parameters + function _toRoleInfo(RoleId adminRoleId, RoleType roleType, uint32 maxMemberCount, string memory name) internal view returns (RoleInfo memory info) { + return RoleInfo({ + name: StrLib.toStr(name), + adminRoleId: adminRoleId, + roleType: roleType, + maxMemberCount: maxMemberCount, + createdAt: TimestampLib.blockTimestamp(), + pausedAt: TimestampLib.max()}); + } + + /// @dev Use this method to authorize the specified domain to access the service domain. - function _authorizeForService(ObjectType serviceDomain, ObjectType authorizedDomain) + function _authorizeForService( + ObjectType serviceDomain, + ObjectType authorizedDomain + ) internal returns (IAccess.FunctionInfo[] storage authorizatedFunctions) { - _authorizedDomains[serviceDomain].push(authorizedDomain); - return _authorizedFunctions[serviceDomain][authorizedDomain]; + Str serviceTarget = getServiceTarget(serviceDomain); + RoleId authorizedRoleId = getServiceRole(authorizedDomain); + + return _authorizeForTarget(serviceTarget.toString(), authorizedRoleId); } + + /// @dev Use this method to authorize the specified role to access the target. + function _authorizeForTarget(string memory target, RoleId authorizedRoleId) + internal + returns (IAccess.FunctionInfo[] storage authorizatedFunctions) + { + Str targetStr = StrLib.toStr(target); + + // add authorized role if not already added + if (_authorizedFunctions[targetStr][authorizedRoleId].length == 0) { + _authorizedRoles[targetStr].push(authorizedRoleId); + } + + return _authorizedFunctions[targetStr][authorizedRoleId]; + } + + /// @dev Use this method to authorize a specific function authorization function _authorize(IAccess.FunctionInfo[] storage functions, bytes4 selector, string memory name) internal { functions.push( diff --git a/contracts/distribution/BasicDistributionAuthorization.sol b/contracts/distribution/BasicDistributionAuthorization.sol index 08e2f81d7..fff9c3309 100644 --- a/contracts/distribution/BasicDistributionAuthorization.sol +++ b/contracts/distribution/BasicDistributionAuthorization.sol @@ -8,7 +8,7 @@ import {Authorization} from "../authorization/Authorization.sol"; import {BasicDistribution} from "./BasicDistribution.sol"; import {Distribution} from "./Distribution.sol"; import {COMPONENT, DISTRIBUTION} from "../type/ObjectType.sol"; -import {RoleId, PUBLIC_ROLE} from "../type/RoleId.sol"; +import {PUBLIC_ROLE} from "../type/RoleId.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; @@ -16,15 +16,22 @@ contract BasicDistributionAuthorization is Authorization { - constructor(string memory distributionlName) - Authorization(distributionlName, DISTRIBUTION(), true, true) + constructor(string memory distributionName) + Authorization( + distributionName, + DISTRIBUTION(), + 3, + COMMIT_HASH, + true, + true) {} function _setupServiceTargets() internal virtual override { - _addServiceTargetWithRole(COMPONENT()); + _authorizeServiceDomain(COMPONENT(), address(11)); + _authorizeServiceDomain(DISTRIBUTION(), address(11)); } function _setupTokenHandlerAuthorizations() internal virtual override { diff --git a/contracts/distribution/Distribution.sol b/contracts/distribution/Distribution.sol index 6d92f435b..b04ef9ff7 100644 --- a/contracts/distribution/Distribution.sol +++ b/contracts/distribution/Distribution.sol @@ -37,6 +37,7 @@ abstract contract Distribution is external virtual restricted() + // solhint-disable-next-line no-empty-blocks { // default is no action } @@ -81,7 +82,7 @@ abstract contract Distribution is function calculateRenewalFeeAmount( - ReferralId referralId, + ReferralId, // referralId uint256 netPremiumAmount ) external diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index 8083716f6..fb0975b25 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -12,7 +12,7 @@ import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; import {NftId} from "../type/NftId.sol"; import {ObjectType, INSTANCE, ORACLE} from "../type/ObjectType.sol"; -import {RoleId, RoleIdLib} from "../type/RoleId.sol"; +import {RoleId, RoleIdLib, ADMIN_ROLE} from "../type/RoleId.sol"; import {Str, StrLib} from "../type/String.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; import {TimestampLib} from "../type/Timestamp.sol"; @@ -45,7 +45,8 @@ contract InstanceAdmin is IInstance internal _instance; IRegistry internal _registry; VersionPart internal _release; - uint64 internal _customIdNext; + + uint64 internal _customRoleIdNext; mapping(address target => RoleId roleId) internal _targetRoleId; uint64 internal _components; @@ -96,28 +97,73 @@ contract InstanceAdmin is _instance = IInstance(instance); _authorization = IAuthorization(authorization); _components = 0; - _customIdNext = 0; + _customRoleIdNext = 0; // link nft ownability to instance _linkToNftOwnable(instance); - // create instance role and target - _setupInstance(instance); + // TODO cleanup + // // create instance role and target + // _setupInstance(instance); + + _setupServiceRoles(_authorization); + + _createTargets(_authorization); // add instance authorization _createRoles(_authorization); - _setupInstanceHelperTargetsWithRoles(); + + // _setupInstanceHelperTargetsWithRoles(); _createTargetAuthorizations(_authorization); } + /// @dev grants the service roles to the service addresses based on the authorization specification. + /// Service addresses used for the granting are determined by the registry and the release of this instance. + function _setupServiceRoles(IAuthorization authorization) + internal + { + ObjectType[] memory serviceDomains = authorization.getServiceDomains(); + + for(uint256 i = 0; i < serviceDomains.length; i++) { + ObjectType serviceDomain = serviceDomains[i]; + RoleId serviceRoleId = authorization.getServiceRole(serviceDomain); + string memory serviceRoleName = authorization.getRoleName(serviceRoleId); + + // create service role if missing + if (!roleExists(serviceRoleId)) { + _createRole( + serviceRoleId, + AccessAdminLib.toRole( + ADMIN_ROLE(), + IAccess.RoleType.Contract, + 1, + serviceRoleName)); + } + + // grant service role to service + address service = _registry.getServiceAddress(serviceDomain, _release); + _grantRoleToAccount( + serviceRoleId, + service); + } + } + + function _createTargets(IAuthorization authorization) internal { - _createTargetWithRole(address(this), INSTANCE_ADMIN_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(INSTANCE_ADMIN_TARGET_NAME))); - _createTargetWithRole(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(INSTANCE_STORE_TARGET_NAME))); - _createTargetWithRole(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(BUNDLE_SET_TARGET_NAME))); - _createTargetWithRole(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(RISK_SET_TARGET_NAME))); + // TODO cleanup + // _createTargetWithRole(address(this), INSTANCE_ADMIN_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(INSTANCE_ADMIN_TARGET_NAME))); + // _createTargetWithRole(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(INSTANCE_STORE_TARGET_NAME))); + // _createTargetWithRole(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(BUNDLE_SET_TARGET_NAME))); + // _createTargetWithRole(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(RISK_SET_TARGET_NAME))); + + _createManagedTarget(address(_instance), authorization.getMainTargetName(), TargetType.Instance); + _createManagedTarget(address(this), INSTANCE_ADMIN_TARGET_NAME, TargetType.Instance); + _createManagedTarget(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME, TargetType.Instance); + _createManagedTarget(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME, TargetType.Instance); + _createManagedTarget(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME, TargetType.Instance); } @@ -138,12 +184,12 @@ contract InstanceAdmin is _checkAuthorization(address(authorization), expectedType, getRelease(), false); // effects - // setup target and role for component (including token handler if applicable) - _setupComponentAndTokenHandler(component, expectedType); - - // create other roles and function authorizations _createRoles(authorization); + _createManagedTarget(componentAddress, authorization.getMainTargetName(), TargetType.Component); _createTargetAuthorizations(authorization); + + // increase component count + _components++; } function getRelease() @@ -155,30 +201,30 @@ contract InstanceAdmin is return _release; } + // TODO cleanup + // // create instance role and target + // function _setupInstance(address instance) internal { - // create instance role and target - function _setupInstance(address instance) internal { + // // create instance role + // RoleId instanceRoleId = _authorization.getTargetRole( + // _authorization.getMainTarget()); - // create instance role - RoleId instanceRoleId = _authorization.getTargetRole( - _authorization.getMainTarget()); + // _createRole( + // instanceRoleId, + // _authorization.getRoleInfo(instanceRoleId)); - _createRole( - instanceRoleId, - _authorization.getRoleInfo(instanceRoleId)); - - // create instance target - _createTarget( - instance, - _authorization.getMainTargetName(), - true, // checkAuthority - false); // custom - - // assign instance role to instance - _grantRoleToAccount( - instanceRoleId, - instance); - } + // // create instance target + // _createTarget( + // instance, + // _authorization.getMainTargetName(), + // true, // checkAuthority + // false); // custom + + // // assign instance role to instance + // _grantRoleToAccount( + // instanceRoleId, + // instance); + // } /// @dev Creates a custom role. @@ -191,7 +237,10 @@ contract InstanceAdmin is restricted() returns (RoleId roleId) { - roleId = RoleIdLib.toCustomRoleId(_customIdNext++); + // create roleId + roleId = AccessAdminLib.getCustomRoleId(_customRoleIdNext++); + + // create role _createRole( roleId, AccessAdminLib.toRole( @@ -235,29 +284,26 @@ contract InstanceAdmin is /// @dev Create a new custom target. /// The target needs to be an access managed contract. - /// The target role parameter is optional, set to zero if not required. - /// Only custom roles may be assigned to custom targets. function createTarget( address target, - RoleId targetRoleId, string memory name ) external restricted() { - _createTarget( + _createManagedTarget( target, name, - true, // checkAuthority - true); // custom + TargetType.Custom); - if (targetRoleId != RoleIdLib.zero()) { - if (getRoleInfo(targetRoleId).roleType != IAccess.RoleType.Custom) { - revert ErrorInstanceAdminNotCustomRole(targetRoleId); - } + // TODO cleanup + // if (targetRoleId != RoleIdLib.zero()) { + // if (getRoleInfo(targetRoleId).roleType != IAccess.RoleType.Custom) { + // revert ErrorInstanceAdminNotCustomRole(targetRoleId); + // } - _grantRoleToAccount(targetRoleId, target); - } + // _grantRoleToAccount(targetRoleId, target); + // } } @@ -307,98 +353,99 @@ contract InstanceAdmin is // ------------------- Internal functions ------------------- // - function _setupComponentAndTokenHandler( - IInstanceLinkedComponent component, - ObjectType componentType - ) - internal - { - - IAuthorization authorization = component.getAuthorization(); - string memory targetName = authorization.getMainTargetName(); - - // create component role and target - RoleId componentRoleId = _createComponentRoleId(component, authorization); - - // create component's target - _createTarget( - address(component), - targetName, - true, // checkAuthority - false); // custom - - // create component's token handler target if app - if (componentType != ORACLE()) { - NftId componentNftId = _registry.getNftIdForAddress(address(component)); - address tokenHandler = address( - _instance.getInstanceReader().getComponentInfo( - componentNftId).tokenHandler); - - _createTarget( - tokenHandler, - authorization.getTokenHandlerName(), - true, - false); - - // token handler does not require its own role - // token handler is not calling other components - } - - // assign component role to component - _grantRoleToAccount( - componentRoleId, - address(component)); - } - - - function _createComponentRoleId( - IInstanceLinkedComponent component, - IAuthorization authorization - ) - internal - returns (RoleId componentRoleId) - { - // checks - // check component is not yet authorized - if (_targetRoleId[address(component)].gtz()) { - revert ErrorInstanceAdminAlreadyAuthorized(address(component)); - } - - // check generic component role - RoleId genericComponentRoleId = authorization.getTargetRole( - authorization.getMainTarget()); - - if (!genericComponentRoleId.isComponentRole()) { - revert ErrorInstanceAdminNotComponentRole(genericComponentRoleId); - } - - // check component role does not exist - componentRoleId = toComponentRole( - genericComponentRoleId, - _components); - - if (roleExists(componentRoleId)) { - revert ErrorInstanceAdminRoleAlreadyExists(componentRoleId); - } - - // check role info - IAccess.RoleInfo memory roleInfo = authorization.getRoleInfo( - genericComponentRoleId); - - if (roleInfo.roleType != IAccess.RoleType.Contract) { - revert ErrorInstanceAdminRoleTypeNotContract( - componentRoleId, - roleInfo.roleType); - } - - // effects - _targetRoleId[address(component)] = componentRoleId; - _components++; - - _createRole( - componentRoleId, - roleInfo); - } + // TODO cleanup + // function _setupComponentAndTokenHandler( + // IInstanceLinkedComponent component, + // ObjectType componentType + // ) + // internal + // { + + // IAuthorization authorization = component.getAuthorization(); + // string memory targetName = authorization.getMainTargetName(); + + // // create component role and target + // RoleId componentRoleId = _createComponentRoleId(component, authorization); + + // // create component's target + // _createTarget( + // address(component), + // targetName, + // true, // checkAuthority + // false); // custom + + // // create component's token handler target if app + // if (componentType != ORACLE()) { + // NftId componentNftId = _registry.getNftIdForAddress(address(component)); + // address tokenHandler = address( + // _instance.getInstanceReader().getComponentInfo( + // componentNftId).tokenHandler); + + // _createTarget( + // tokenHandler, + // authorization.getTokenHandlerName(), + // true, + // false); + + // // token handler does not require its own role + // // token handler is not calling other components + // } + + // // assign component role to component + // _grantRoleToAccount( + // componentRoleId, + // address(component)); + // } + + + // function _createComponentRoleId( + // IInstanceLinkedComponent component, + // IAuthorization authorization + // ) + // internal + // returns (RoleId componentRoleId) + // { + // // checks + // // check component is not yet authorized + // if (_targetRoleId[address(component)].gtz()) { + // revert ErrorInstanceAdminAlreadyAuthorized(address(component)); + // } + + // // check generic component role + // RoleId genericComponentRoleId = authorization.getTargetRole( + // authorization.getMainTarget()); + + // if (!genericComponentRoleId.isComponentRole()) { + // revert ErrorInstanceAdminNotComponentRole(genericComponentRoleId); + // } + + // // check component role does not exist + // componentRoleId = toComponentRole( + // genericComponentRoleId, + // _components); + + // if (roleExists(componentRoleId)) { + // revert ErrorInstanceAdminRoleAlreadyExists(componentRoleId); + // } + + // // check role info + // IAccess.RoleInfo memory roleInfo = authorization.getRoleInfo( + // genericComponentRoleId); + + // if (roleInfo.roleType != IAccess.RoleType.Contract) { + // revert ErrorInstanceAdminRoleTypeNotContract( + // componentRoleId, + // roleInfo.roleType); + // } + + // // effects + // _targetRoleId[address(component)] = componentRoleId; + // _components++; + + // _createRole( + // componentRoleId, + // roleInfo); + // } function _createRoles(IAuthorization authorization) @@ -408,12 +455,8 @@ contract InstanceAdmin is RoleId mainTargetRoleId = authorization.getTargetRole( authorization.getMainTarget()); - RoleId roleId; - RoleInfo memory roleInfo; - for(uint256 i = 0; i < roles.length; i++) { - - roleId = roles[i]; + RoleId roleId = roles[i]; // skip main target role, create role if not exists if (roleId != mainTargetRoleId && !roleExists(roleId)) { @@ -438,75 +481,88 @@ contract InstanceAdmin is function _createTargetAuthorizations(IAuthorization authorization) internal { + // TODO cleanup + // Str[] memory targets = authorization.getTargets(); + // Str target; + + // for(uint256 i = 0; i < targets.length; i++) { + // target = targets[i]; + // RoleId[] memory authorizedRoles = authorization.getAuthorizedRoles(target); + // RoleId authorizedRole; + + // for(uint256 j = 0; j < authorizedRoles.length; j++) { + // authorizedRole = authorizedRoles[j]; + + // _authorizeTargetFunctions( + // getTargetForName(target), + // authorizedRole, + // authorization.getAuthorizedFunctions( + // target, + // authorizedRole), + // true); + // } + // } Str[] memory targets = authorization.getTargets(); Str target; for(uint256 i = 0; i < targets.length; i++) { target = targets[i]; RoleId[] memory authorizedRoles = authorization.getAuthorizedRoles(target); - RoleId authorizedRole; for(uint256 j = 0; j < authorizedRoles.length; j++) { - authorizedRole = authorizedRoles[j]; - - _authorizeTargetFunctions( - getTargetForName(target), - authorizedRole, - authorization.getAuthorizedFunctions( - target, - authorizedRole), - true); + _authorizeFunctions(authorization, target, authorizedRoles[j]); } } } - function _checkAndCreateTargetWithRole( - address target, - string memory targetName - ) - internal - { - // check that target name is defined in authorization specification - Str name = StrLib.toStr(targetName); - if (!_authorization.targetExists(name)) { - revert ErrorInstanceAdminExpectedTargetMissing(targetName); - } - - // create named target - _createTarget( - target, - targetName, - false, // check authority TODO check normal targets, don't check service targets (they share authority with release admin) - false); - - // assign target role if defined - RoleId targetRoleId = _authorization.getTargetRole(name); - if (targetRoleId != RoleIdLib.zero()) { - _grantRoleToAccount(targetRoleId, target); - } - } - - function _setupInstanceHelperTargetsWithRoles() - internal - { - - // create module targets - _checkAndCreateTargetWithRole(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME); - _checkAndCreateTargetWithRole(address(_instance.getInstanceAdmin()), INSTANCE_ADMIN_TARGET_NAME); - _checkAndCreateTargetWithRole(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME); - _checkAndCreateTargetWithRole(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME); - - // create targets for services that need to access the instance targets - ObjectType[] memory serviceDomains = _authorization.getServiceDomains(); - VersionPart release = _authorization.getRelease(); - ObjectType serviceDomain; - - for (uint256 i = 0; i < serviceDomains.length; i++) { - serviceDomain = serviceDomains[i]; - - _checkAndCreateTargetWithRole( - _registry.getServiceAddress(serviceDomain, release), - _authorization.getServiceTarget(serviceDomain).toString()); - } - } + // TODO cleanup + // function _checkAndCreateTargetWithRole( + // address target, + // string memory targetName + // ) + // internal + // { + // // check that target name is defined in authorization specification + // Str name = StrLib.toStr(targetName); + // if (!_authorization.targetExists(name)) { + // revert ErrorInstanceAdminExpectedTargetMissing(targetName); + // } + + // // create named target + // _createTarget( + // target, + // targetName, + // false, // check authority TODO check normal targets, don't check service targets (they share authority with release admin) + // false); + + // // assign target role if defined + // RoleId targetRoleId = _authorization.getTargetRole(name); + // if (targetRoleId != RoleIdLib.zero()) { + // _grantRoleToAccount(targetRoleId, target); + // } + // } + + // function _setupInstanceHelperTargetsWithRoles() + // internal + // { + + // // create module targets + // _checkAndCreateTargetWithRole(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME); + // _checkAndCreateTargetWithRole(address(_instance.getInstanceAdmin()), INSTANCE_ADMIN_TARGET_NAME); + // _checkAndCreateTargetWithRole(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME); + // _checkAndCreateTargetWithRole(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME); + + // // create targets for services that need to access the instance targets + // ObjectType[] memory serviceDomains = _authorization.getServiceDomains(); + // VersionPart release = _authorization.getRelease(); + // ObjectType serviceDomain; + + // for (uint256 i = 0; i < serviceDomains.length; i++) { + // serviceDomain = serviceDomains[i]; + + // _checkAndCreateTargetWithRole( + // _registry.getServiceAddress(serviceDomain, release), + // _authorization.getServiceTarget(serviceDomain).toString()); + // } + // } } diff --git a/contracts/instance/InstanceAuthorizationV3.sol b/contracts/instance/InstanceAuthorizationV3.sol index 795d04956..e0a974cbe 100644 --- a/contracts/instance/InstanceAuthorizationV3.sol +++ b/contracts/instance/InstanceAuthorizationV3.sol @@ -28,22 +28,28 @@ contract InstanceAuthorizationV3 string public constant RISK_SET_TARGET_NAME = "RiskSet"; constructor() - Authorization(INSTANCE_TARGET_NAME, INSTANCE(), false, false) + Authorization( + INSTANCE_TARGET_NAME, + INSTANCE(), + 3, + COMMIT_HASH, + false, + false) { } function _setupServiceTargets() internal virtual override { // service targets relevant to instance - _addServiceTargetWithRole(INSTANCE()); - _addServiceTargetWithRole(ACCOUNTING()); - _addServiceTargetWithRole(COMPONENT()); - _addServiceTargetWithRole(DISTRIBUTION()); - _addServiceTargetWithRole(ORACLE()); - _addServiceTargetWithRole(POOL()); - _addServiceTargetWithRole(BUNDLE()); - _addServiceTargetWithRole(RISK()); - _addServiceTargetWithRole(APPLICATION()); - _addServiceTargetWithRole(POLICY()); - _addServiceTargetWithRole(CLAIM()); + _authorizeServiceDomain(INSTANCE(), address(10)); + _authorizeServiceDomain(ACCOUNTING(), address(11)); + _authorizeServiceDomain(COMPONENT(), address(12)); + _authorizeServiceDomain(DISTRIBUTION(), address(13)); + _authorizeServiceDomain(ORACLE(), address(14)); + _authorizeServiceDomain(POOL(), address(15)); + _authorizeServiceDomain(BUNDLE(), address(16)); + _authorizeServiceDomain(RISK(), address(17)); + _authorizeServiceDomain(APPLICATION(), address(18)); + _authorizeServiceDomain(POLICY(), address(19)); + _authorizeServiceDomain(CLAIM(), address(20)); } function _setupRoles() diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index 5a47e3aa4..cad32fb44 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -124,7 +124,7 @@ contract InstanceService is instance.getInstanceAdmin().revokeRole(roleId, account); } - + // TODO refactor to not use targetRoleId /// @inheritdoc IInstanceService function createTarget(address target, RoleId targetRoleId, string memory name) external @@ -132,7 +132,7 @@ contract InstanceService is onlyInstance() { IInstance instance = IInstance(msg.sender); - instance.getInstanceAdmin().createTarget(target, targetRoleId, name); + instance.getInstanceAdmin().createTarget(target, name); } diff --git a/contracts/oracle/BasicOracleAuthorization.sol b/contracts/oracle/BasicOracleAuthorization.sol index 423ff6936..c200e6c61 100644 --- a/contracts/oracle/BasicOracleAuthorization.sol +++ b/contracts/oracle/BasicOracleAuthorization.sol @@ -14,10 +14,26 @@ contract BasicOracleAuthorization is Authorization { - constructor(string memory componentName) - Authorization(componentName, ORACLE(), true, false) + constructor( + string memory componentName, + string memory commitHash + ) + Authorization( + componentName, + ORACLE(), + 3, + commitHash, + true, + false) {} + function _setupServiceTargets() + internal + virtual override + { + _authorizeServiceDomain(ORACLE(), address(15)); + } + function _setupTargetAuthorizations() internal virtual override diff --git a/contracts/pool/BasicPoolAuthorization.sol b/contracts/pool/BasicPoolAuthorization.sol index 19497b892..c54e3d5b3 100644 --- a/contracts/pool/BasicPoolAuthorization.sol +++ b/contracts/pool/BasicPoolAuthorization.sol @@ -18,15 +18,21 @@ contract BasicPoolAuthorization { constructor(string memory poolName) - Authorization(poolName, POOL(), true, true) + Authorization( + poolName, + POOL(), + 3, + COMMIT_HASH, + true, + true) {} function _setupServiceTargets() internal virtual override { - _addServiceTargetWithRole(COMPONENT()); - _addServiceTargetWithRole(POOL()); + _authorizeServiceDomain(COMPONENT(), address(11)); + _authorizeServiceDomain(POOL(), address(12)); } function _setupTokenHandlerAuthorizations() internal virtual override { diff --git a/contracts/product/BasicProductAuthorization.sol b/contracts/product/BasicProductAuthorization.sol index c810af56f..5fb92a74b 100644 --- a/contracts/product/BasicProductAuthorization.sol +++ b/contracts/product/BasicProductAuthorization.sol @@ -14,17 +14,22 @@ import {TokenHandler} from "../shared/TokenHandler.sol"; contract BasicProductAuthorization is Authorization { - constructor(string memory componentName) - Authorization(componentName, PRODUCT(), true, true) + Authorization( + componentName, + PRODUCT(), + 3, + COMMIT_HASH, + true, + true) {} function _setupServiceTargets() internal virtual override { - _addServiceTargetWithRole(COMPONENT()); - _addServiceTargetWithRole(POLICY()); + _authorizeServiceDomain(COMPONENT(), address(10)); + _authorizeServiceDomain(POLICY(), address(11)); } function _setupTokenHandlerAuthorizations() internal virtual override { diff --git a/contracts/product/ClaimService.sol b/contracts/product/ClaimService.sol index 2dff21cd3..93f0ade78 100644 --- a/contracts/product/ClaimService.sol +++ b/contracts/product/ClaimService.sol @@ -68,8 +68,7 @@ contract ClaimService is { // checks ( - , - IInstance instance,, + ,,, InstanceStore instanceStore, IPolicy.PolicyInfo memory policyInfo ) = _verifyCallerWithPolicy(policyNftId); @@ -229,8 +228,7 @@ contract ClaimService is _checkNftType(policyNftId, POLICY()); ( - , - IInstance instance, + ,, InstanceReader instanceReader, InstanceStore instanceStore, IPolicy.PolicyInfo memory policyInfo diff --git a/contracts/registry/RegistryAdmin.sol b/contracts/registry/RegistryAdmin.sol index 2a13cd358..11fd6dd4c 100644 --- a/contracts/registry/RegistryAdmin.sol +++ b/contracts/registry/RegistryAdmin.sol @@ -44,14 +44,16 @@ contract RegistryAdmin is /// @dev gif core roles string public constant GIF_ADMIN_ROLE_NAME = "GifAdminRole"; string public constant GIF_MANAGER_ROLE_NAME = "GifManagerRole"; - string public constant RELEASE_REGISTRY_ROLE_NAME = "ReleaseRegistryRole"; - string public constant STAKING_ROLE_NAME = "StakingRole"; + + // cleanup + // string public constant RELEASE_REGISTRY_ROLE_NAME = "ReleaseRegistryRole"; + // string public constant STAKING_ROLE_NAME = "StakingRole"; /// @dev gif roles for external contracts - string public constant REGISTRY_SERVICE_ROLE_NAME = "RegistryServiceRole"; - string public constant COMPONENT_SERVICE_ROLE_NAME = "ComponentServiceRole"; - string public constant POOL_SERVICE_ROLE_NAME = "PoolServiceRole"; - string public constant STAKING_SERVICE_ROLE_NAME = "StakingServiceRole"; + // string public constant REGISTRY_SERVICE_ROLE_NAME = "RegistryServiceRole"; + // string public constant COMPONENT_SERVICE_ROLE_NAME = "ComponentServiceRole"; + // string public constant POOL_SERVICE_ROLE_NAME = "PoolServiceRole"; + // string public constant STAKING_SERVICE_ROLE_NAME = "StakingServiceRole"; /// @dev gif core targets string public constant REGISTRY_ADMIN_TARGET_NAME = "RegistryAdmin"; @@ -114,19 +116,20 @@ contract RegistryAdmin is // link nft ownability to registry _linkToNftOwnable(_registry); - _setupRegistry(_registry); + // _setupRegistry(_registry); + + // TODO check if targets should be registerd first + _createTargets(_authorization); // setup authorization for registry and supporting contracts _createRoles(_authorization); _grantRoleToAccount(GIF_ADMIN_ROLE(), gifAdmin); _grantRoleToAccount(GIF_MANAGER_ROLE(), gifManager); - _createTargets(_authorization); _createTargetAuthorizations(_authorization); } - function grantServiceRoleForAllVersions( IService service, ObjectType domain @@ -135,7 +138,7 @@ contract RegistryAdmin is restricted() { _grantRoleToAccount( - RoleIdLib.roleForTypeAndAllVersions(domain), + RoleIdLib.toGenericServiceRoleId(domain), address(service)); } @@ -151,50 +154,52 @@ contract RegistryAdmin is //--- private initialization functions -------------------------------------------// - // create registry role and target - function _setupRegistry(address registry) internal { + // TODO cleanup + // // create registry role and target + // function _setupRegistry(address registry) internal { - // create registry role - RoleId roleId = _authorization.getTargetRole( - _authorization.getMainTarget()); + // // create registry role + // RoleId roleId = _authorization.getTargetRole( + // _authorization.getMainTarget()); - _createRole( - roleId, - _authorization.getRoleInfo(roleId)); + // _createRole( + // roleId, + // _authorization.getRoleInfo(roleId)); - // create registry target - _createTarget( - registry, - _authorization.getMainTargetName(), - true, // checkAuthority - false); // custom + // // create registry target + // _createTarget( + // registry, + // _authorization.getMainTargetName(), + // true, // checkAuthority + // false); // custom - // assign registry role to registry - _grantRoleToAccount( - roleId, - registry); - } + // // assign registry role to registry + // _grantRoleToAccount( + // roleId, + // registry); + // } + // TODO cleanup + event LogAccessAdminDebugRole(string name, RoleId roleId, bool exists); function _createRoles(IAuthorization authorization) internal { RoleId[] memory roles = authorization.getRoles(); - RoleId mainTargetRoleId = authorization.getTargetRole( - authorization.getMainTarget()); - - RoleId roleId; - RoleInfo memory roleInfo; for(uint256 i = 0; i < roles.length; i++) { + RoleId roleId = roles[i]; + RoleInfo memory roleInfo = authorization.getRoleInfo(roleId); + + // create role if not exists + bool exists = roleExists(roleInfo.name); - roleId = roles[i]; +emit LogAccessAdminDebugRole(roleInfo.name.toString(), roleId, exists); - // skip main target role, create role if not exists - if (roleId != mainTargetRoleId && !roleExists(roleId)) { + if (!exists) { _createRole( roleId, - authorization.getRoleInfo(roleId)); + roleInfo); } } } @@ -203,17 +208,26 @@ contract RegistryAdmin is function _createTargets(IAuthorization authorization) internal { + // TODO cleanup // registry - _createTargetWithRole(address(this), REGISTRY_ADMIN_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(REGISTRY_ADMIN_TARGET_NAME))); - _createTargetWithRole(_releaseRegistry, RELEASE_REGISTRY_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(RELEASE_REGISTRY_TARGET_NAME))); - _createTargetWithRole(_tokenRegistry, TOKEN_REGISTRY_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(TOKEN_REGISTRY_TARGET_NAME))); + // _createTargetWithRole(address(this), REGISTRY_ADMIN_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(REGISTRY_ADMIN_TARGET_NAME))); + // _createTargetWithRole(_releaseRegistry, RELEASE_REGISTRY_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(RELEASE_REGISTRY_TARGET_NAME))); + // _createTargetWithRole(_tokenRegistry, TOKEN_REGISTRY_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(TOKEN_REGISTRY_TARGET_NAME))); + + _createUncheckedTarget(_registry, authorization.getMainTargetName(), TargetType.Core); + _createUncheckedTarget(address(this), REGISTRY_ADMIN_TARGET_NAME, TargetType.Core); + _createUncheckedTarget(_releaseRegistry, RELEASE_REGISTRY_TARGET_NAME, TargetType.Core); + _createUncheckedTarget(_tokenRegistry, TOKEN_REGISTRY_TARGET_NAME, TargetType.Core); // staking - _createTargetWithRole(_staking, STAKING_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(STAKING_TARGET_NAME))); - _createTarget(_stakingStore, STAKING_STORE_TARGET_NAME, true, false); - _createTarget(address(IComponent(_staking).getTokenHandler()), STAKING_TH_TARGET_NAME, true, false); + // _createTargetWithRole(_staking, STAKING_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(STAKING_TARGET_NAME))); + // _createTarget(_stakingStore, STAKING_STORE_TARGET_NAME, true, false); + // _createTarget(address(IComponent(_staking).getTokenHandler()), STAKING_TH_TARGET_NAME, true, false); + _createUncheckedTarget(_staking, STAKING_TARGET_NAME, TargetType.Core); + _createUncheckedTarget(_stakingStore, STAKING_STORE_TARGET_NAME, TargetType.Core); } + // TODO cleanup function _createTargetAuthorizations(IAuthorization authorization) internal @@ -224,36 +238,54 @@ contract RegistryAdmin is for(uint256 i = 0; i < targets.length; i++) { target = targets[i]; RoleId[] memory authorizedRoles = authorization.getAuthorizedRoles(target); - RoleId authorizedRole; for(uint256 j = 0; j < authorizedRoles.length; j++) { - authorizedRole = authorizedRoles[j]; - - _authorizeTargetFunctions( - getTargetForName(target), - authorizedRole, - authorization.getAuthorizedFunctions( - target, - authorizedRole), - true); + _authorizeFunctions(authorization, target, authorizedRoles[j]); } } } - - function _createTargets(address authorization) - private - onlyInitializing() - { - IStaking staking = IStaking(_staking); - address tokenHandler = address(staking.getTokenHandler()); - - _createTarget(address(this), REGISTRY_ADMIN_TARGET_NAME, false, false); - _createTarget(_registry, REGISTRY_TARGET_NAME, true, false); - _createTarget(_releaseRegistry, RELEASE_REGISTRY_TARGET_NAME, true, false); - _createTarget(_staking, STAKING_TARGET_NAME, true, false); - _createTarget(_stakingStore, STAKING_STORE_TARGET_NAME, true, false); - _createTarget(_tokenRegistry, TOKEN_REGISTRY_TARGET_NAME, true, false); - _createTarget(tokenHandler, TOKEN_HANDLER_TARGET_NAME, true, false); - } + // TODO cleanup +// event LogAccessAdminDebugTarget(string name, address target, RoleId roleId); +// function _authorizeFunctions(IAuthorization authorization, RoleId roleId) +// internal +// { +// RoleId authorizedRoleId = _getAuthorizedRoleId(authorization, roleId); +// address getTargetAddress = getTargetForName(target); +// emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, authorizedRoleId); + +// _authorizeTargetFunctions( +// getTargetForName(target), +// authorizedRoleId, +// authorization.getAuthorizedFunctions( +// target, +// roleId), +// true); +// } + +// function _getAuthorizedRoleId(IAuthorization authorization, RoleId roleId) +// internal +// view +// returns (RoleId authorizedRoleId) +// { +// string memory roleName = authorization.getRoleInfo(roleId).name.toString(); +// return authorizedRoleId = getRoleForName(roleName); +// } + + // TODO cleanup + // function _createTargets(address authorization) + // private + // onlyInitializing() + // { + // IStaking staking = IStaking(_staking); + // address tokenHandler = address(staking.getTokenHandler()); + + // _createTarget(address(this), REGISTRY_ADMIN_TARGET_NAME, false, false, false); + // _createTarget(_registry, REGISTRY_TARGET_NAME, true, false, false); + // _createTarget(_releaseRegistry, RELEASE_REGISTRY_TARGET_NAME, true, false); + // _createTarget(_staking, STAKING_TARGET_NAME, true, false); + // _createTarget(_stakingStore, STAKING_STORE_TARGET_NAME, true, false); + // _createTarget(_tokenRegistry, TOKEN_REGISTRY_TARGET_NAME, true, false); + // _createTarget(tokenHandler, TOKEN_HANDLER_TARGET_NAME, true, false); + // } } \ No newline at end of file diff --git a/contracts/registry/RegistryAuthorization.sol b/contracts/registry/RegistryAuthorization.sol index afc13c319..21fdf770a 100644 --- a/contracts/registry/RegistryAuthorization.sol +++ b/contracts/registry/RegistryAuthorization.sol @@ -14,6 +14,7 @@ import {RoleIdLib, ADMIN_ROLE, GIF_ADMIN_ROLE, GIF_MANAGER_ROLE} from "../type/R import {StakingStore} from "../staking/StakingStore.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; import {TokenRegistry} from "../registry/TokenRegistry.sol"; +import {VersionPartLib} from "../type/Version.sol"; contract RegistryAuthorization @@ -39,39 +40,48 @@ contract RegistryAuthorization string public constant TOKEN_REGISTRY_TARGET_NAME = "TokenRegistry"; string public constant STAKING_TARGET_NAME = "Staking"; - string public constant STAKING_TH_TARGET_NAME = "StakingTH"; + string public constant STAKING_TH_TARGET_NAME = "StakingTh"; string public constant STAKING_STORE_TARGET_NAME = "StakingStore"; - constructor() - Authorization(REGISTRY_TARGET_NAME, REGISTRY(), false, false) + constructor(string memory commitHash) + Authorization( + REGISTRY_TARGET_NAME, + REGISTRY(), + 3, + commitHash, + false, // isComponent + false) // includeTokenHandler { } /// @dev Sets up the GIF admin and manager roles. function _setupRoles() internal override { - // service roles (for all versions) + // max number of versioned contracts per generic service + uint32 maxReleases = uint32(VersionPartLib.releaseMax().toInt()); + + // service roles (for all releases) _addRole( - RoleIdLib.roleForTypeAndAllVersions(REGISTRY()), + RoleIdLib.toGenericServiceRoleId(REGISTRY()), _toRoleInfo({ adminRoleId: ADMIN_ROLE(), - roleType: RoleType.Gif, - maxMemberCount: 999, // max member count = max + roleType: RoleType.Core, + maxMemberCount: maxReleases, name: REGISTRY_SERVICE_ROLE_NAME})); _addRole( - RoleIdLib.roleForTypeAndAllVersions(STAKING()), + RoleIdLib.toGenericServiceRoleId(STAKING()), _toRoleInfo({ adminRoleId: ADMIN_ROLE(), - roleType: RoleType.Gif, - maxMemberCount: 999, // max member count = max + roleType: RoleType.Core, + maxMemberCount: maxReleases, name: STAKING_SERVICE_ROLE_NAME})); _addRole( - RoleIdLib.roleForTypeAndAllVersions(POOL()), + RoleIdLib.toGenericServiceRoleId(POOL()), _toRoleInfo({ adminRoleId: ADMIN_ROLE(), - roleType: RoleType.Gif, - maxMemberCount: 999, // max member count = max + roleType: RoleType.Core, + maxMemberCount: maxReleases, name: POOL_SERVICE_ROLE_NAME})); // gif admin role @@ -79,7 +89,7 @@ contract RegistryAuthorization GIF_ADMIN_ROLE(), _toRoleInfo({ adminRoleId: ADMIN_ROLE(), - roleType: RoleType.Gif, + roleType: RoleType.Core, maxMemberCount: 2, // TODO decide on max member count name: GIF_ADMIN_ROLE_NAME})); @@ -88,7 +98,7 @@ contract RegistryAuthorization GIF_MANAGER_ROLE(), _toRoleInfo({ adminRoleId: ADMIN_ROLE(), - roleType: RoleType.Gif, + roleType: RoleType.Core, maxMemberCount: 1, // TODO decide on max member count name: GIF_MANAGER_ROLE_NAME})); @@ -96,13 +106,13 @@ contract RegistryAuthorization /// @dev Sets up the relevant (non-service) targets for the registry. function _setupTargets() internal override { - _addGifContractTarget(REGISTRY_ADMIN_TARGET_NAME); - _addGifContractTarget(RELEASE_REGISTRY_TARGET_NAME); - _addGifContractTarget(TOKEN_REGISTRY_TARGET_NAME); + _addGifTarget(REGISTRY_ADMIN_TARGET_NAME); + _addGifTarget(RELEASE_REGISTRY_TARGET_NAME); + _addGifTarget(TOKEN_REGISTRY_TARGET_NAME); - _addGifContractTarget(STAKING_TARGET_NAME); - _addGifContractTarget(STAKING_TH_TARGET_NAME); - _addTarget(STAKING_STORE_TARGET_NAME); + _addGifTarget(STAKING_TARGET_NAME); + _addGifTarget(STAKING_TH_TARGET_NAME); + _addGifTarget(STAKING_STORE_TARGET_NAME); } @@ -134,7 +144,7 @@ contract RegistryAuthorization // registry service role functions = _authorizeForTarget( REGISTRY_TARGET_NAME, - RoleIdLib.roleForTypeAndAllVersions(REGISTRY())); + RoleIdLib.toGenericServiceRoleId(REGISTRY())); _authorize(functions, IRegistry.register.selector, "register"); _authorize(functions, IRegistry.registerWithCustomType.selector, "registerWithCustomType"); @@ -201,7 +211,7 @@ contract RegistryAuthorization // staking service role functions = _authorizeForTarget( STAKING_TARGET_NAME, - RoleIdLib.roleForTypeAndAllVersions(STAKING())); + RoleIdLib.toGenericServiceRoleId(STAKING())); _authorize(functions, IStaking.registerTarget.selector, "registerTarget"); _authorize(functions, IStaking.setLockingPeriod.selector, "setLockingPeriod"); @@ -219,7 +229,7 @@ contract RegistryAuthorization // pool service role functions = _authorizeForTarget( STAKING_TARGET_NAME, - RoleIdLib.roleForTypeAndAllVersions(POOL())); + RoleIdLib.toGenericServiceRoleId(POOL())); _authorize(functions, IStaking.increaseTotalValueLocked.selector, "increaseTotalValueLocked"); _authorize(functions, IStaking.decreaseTotalValueLocked.selector, "decreaseTotalValueLocked"); @@ -232,7 +242,7 @@ contract RegistryAuthorization // staking service role functions = _authorizeForTarget( STAKING_TH_TARGET_NAME, - RoleIdLib.roleForTypeAndAllVersions(STAKING())); + RoleIdLib.toGenericServiceRoleId(STAKING())); _authorize(functions, TokenHandler.approve.selector, "approve"); _authorize(functions, TokenHandler.pullToken.selector, "pullToken"); diff --git a/contracts/registry/ReleaseAdmin.sol b/contracts/registry/ReleaseAdmin.sol index dd556e833..a55f3387c 100644 --- a/contracts/registry/ReleaseAdmin.sol +++ b/contracts/registry/ReleaseAdmin.sol @@ -1,14 +1,17 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {AccessAdmin} from "../authorization/AccessAdmin.sol"; -import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; -import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; import {IAccess} from "../authorization/IAccess.sol"; +import {IAuthorization} from "../authorization/IAuthorization.sol"; import {IService} from "../shared/IService.sol"; import {IServiceAuthorization} from "../authorization/IServiceAuthorization.sol"; + +import {AccessAdmin} from "../authorization/AccessAdmin.sol"; +import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; +import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; import {ObjectType, ObjectTypeLib, ALL} from "../type/ObjectType.sol"; import {RoleId, RoleIdLib, ADMIN_ROLE, RELEASE_REGISTRY_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; +import {Str, StrLib} from "../type/String.sol"; import {VersionPart} from "../type/Version.sol"; /// @dev The ReleaseAdmin contract implements the central authorization for the services of a specific release. @@ -25,7 +28,7 @@ contract ReleaseAdmin is error ErrorReleaseAdminReleaseLockAlreadySetTo(bool locked); /// @dev release core roles - string public constant RELEASE_REGISTRY_ROLE_NAME = "ReleaseRegistryRole"; + string public constant RELEASE_REGISTRY_ROLE_NAME = "ReleaseRegistry_Role"; /// @dev release core targets string public constant RELEASE_ADMIN_TARGET_NAME = "ReleaseAdmin"; @@ -145,33 +148,34 @@ contract ReleaseAdmin is string memory serviceTargetName = ObjectTypeLib.toVersionedName( baseName, "Service", release); - _createTarget( - address(service), - serviceTargetName, - true, - false); - - // create service role - RoleId serviceRoleId = RoleIdLib.roleForTypeAndVersion( - serviceDomain, - release); - - if(!roleExists(serviceRoleId)) { - _createRole( - serviceRoleId, - AccessAdminLib.toRole({ - adminRoleId: ADMIN_ROLE(), - roleType: RoleType.Contract, - maxMemberCount: 1, - name: ObjectTypeLib.toVersionedName( - baseName, - "ServiceRole", - release)})); - } - - _grantRoleToAccount( - serviceRoleId, - address(service)); + _createUncheckedTarget(address(service), serviceTargetName, TargetType.Service); + + // _createManagedTarget( + // address(service), + // serviceTargetName, + // IAccess.TargetType.Service); + + // // create service role + // RoleId serviceRoleId = RoleIdLib.toServiceRoleId( + // serviceDomain, + // release); + + // if(!roleExists(serviceRoleId)) { + // _createRole( + // serviceRoleId, + // AccessAdminLib.toRole({ + // adminRoleId: ADMIN_ROLE(), + // roleType: RoleType.Contract, + // maxMemberCount: 1, + // name: ObjectTypeLib.toVersionedName( + // baseName, + // "ServiceRole", + // release)})); + // } + + // _grantRoleToAccount( + // serviceRoleId, + // address(service)); } @@ -186,54 +190,64 @@ contract ReleaseAdmin is ObjectType authorizedDomain; RoleId authorizedRoleId; - ObjectType[] memory authorizedDomains = serviceAuthorization.getAuthorizedDomains(serviceDomain); - - for (uint256 i = 0; i < authorizedDomains.length; i++) { - authorizedDomain = authorizedDomains[i]; - - // derive authorized role from authorized domain - if (authorizedDomain == ALL()) { - authorizedRoleId = PUBLIC_ROLE(); - } else { - authorizedRoleId = RoleIdLib.roleForTypeAndVersion( - authorizedDomain, - release); - } - - if(!roleExists(authorizedRoleId)) { - // create role for authorized domain - _createRole( - authorizedRoleId, - AccessAdminLib.toRole({ - adminRoleId: ADMIN_ROLE(), - roleType: RoleType.Contract, - maxMemberCount: 1, - name: ObjectTypeLib.toVersionedName( - ObjectTypeLib.toName(authorizedDomain), - "Role", - release)})); - } - - // get authorized functions for authorized domain - IAccess.FunctionInfo[] memory authorizatedFunctions = serviceAuthorization.getAuthorizedFunctions( - serviceDomain, - authorizedDomain); - - _authorizeTargetFunctions( - address(service), - authorizedRoleId, - authorizatedFunctions, - true); + Str serviceTarget = serviceAuthorization.getServiceTarget(serviceDomain); + RoleId[] memory authorizedRoles = serviceAuthorization.getAuthorizedRoles(serviceTarget); + + for (uint256 i = 0; i < authorizedRoles.length; i++) { + // authorizedDomain = authorizedDomains[i]; + + // // derive authorized role from authorized domain + // if (authorizedDomain == ALL()) { + // authorizedRoleId = PUBLIC_ROLE(); + // } else { + // authorizedRoleId = RoleIdLib.toServiceRoleId( + // authorizedDomain, + // release); + // } + + // Str target = StrLib.toStr(ObjectTypeLib.toName(service.getDomain())); + + _authorizeFunctions( + IAuthorization(address(serviceAuthorization)), + serviceTarget, + authorizedRoles[i]); + + // TODO cleanup + // if(!roleExists(authorizedRoleId)) { + // // create role for authorized domain + // _createRole( + // authorizedRoleId, + // AccessAdminLib.toRole({ + // adminRoleId: ADMIN_ROLE(), + // roleType: RoleType.Contract, + // maxMemberCount: 1, + // name: ObjectTypeLib.toVersionedName( + // ObjectTypeLib.toName(authorizedDomain), + // "Role", + // release)})); + // } + + // // get authorized functions for authorized domain + // IAccess.FunctionInfo[] memory authorizatedFunctions = serviceAuthorization.getAuthorizedFunctions( + // serviceDomain, + // authorizedDomain); + + // _authorizeTargetFunctions( + // address(service), + // authorizedRoleId, + // authorizatedFunctions, + // true); } } //--- private initialization functions -------------------------------------------// + // TODO check if this can be moved to a standard Authorization setup function _setupReleaseRegistry(address releaseRegistry) private onlyInitializing() { - _createTarget(address(this), RELEASE_ADMIN_TARGET_NAME, false, false); + _createManagedTarget(address(this), RELEASE_ADMIN_TARGET_NAME, IAccess.TargetType.Core); _createRole( RELEASE_REGISTRY_ROLE(), diff --git a/contracts/registry/ReleaseRegistry.sol b/contracts/registry/ReleaseRegistry.sol index 2d8d554dd..5fb525946 100644 --- a/contracts/registry/ReleaseRegistry.sol +++ b/contracts/registry/ReleaseRegistry.sol @@ -182,8 +182,8 @@ contract ReleaseRegistry is checkTransition(_releaseInfo[releaseVersion].state, RELEASE(), DEPLOYING(), DEPLOYED()); address releaseAuthority = ReleaseAdmin(_releaseInfo[releaseVersion].releaseAdmin).authority(); - IServiceAuthorization releaseAuth = _releaseInfo[releaseVersion].auth; - ObjectType expectedDomain = releaseAuth.getServiceDomain(_registeredServices); + IServiceAuthorization releaseAuthz = _releaseInfo[releaseVersion].auth; + ObjectType expectedDomain = releaseAuthz.getServiceDomain(_registeredServices); // service can work with release registry and release version ( @@ -216,7 +216,7 @@ contract ReleaseRegistry is ReleaseAdmin releaseAdmin = ReleaseAdmin(_releaseInfo[releaseVersion].releaseAdmin); releaseAdmin.setReleaseLocked(false); releaseAdmin.authorizeService( - releaseAuth, + releaseAuthz, service, serviceDomain, releaseVersion); @@ -412,7 +412,7 @@ contract ReleaseRegistry is returns (uint256 serviceDomainsCount) { // authorization contract supports IServiceAuthorization interface - if(!serviceAuthorization.supportsInterface(type(IServiceAuthorization).interfaceId)) { + if(!ContractLib.supportsInterface(address(serviceAuthorization), type(IServiceAuthorization).interfaceId)) { revert ErrorReleaseRegistryNotServiceAuth(address(serviceAuthorization)); } diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index 267a90350..b5fc8a5d1 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import { - ALL, ACCOUNTING, REGISTRY, RISK, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, APPLICATION, POLICY, CLAIM, BUNDLE, STAKING, PRICE + ALL, RELEASE, ACCOUNTING, REGISTRY, RISK, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, APPLICATION, POLICY, CLAIM, BUNDLE, STAKING, PRICE } from "../../contracts/type/ObjectType.sol"; import {IAccess} from "../authorization/IAccess.sol"; @@ -19,6 +19,7 @@ import {IPoolService} from "../pool/IPoolService.sol"; import {IStakingService} from "../staking/IStakingService.sol"; import {IRegistryService} from "./IRegistryService.sol"; import {IRiskService} from "../product/IRiskService.sol"; + import {ServiceAuthorization} from "../authorization/ServiceAuthorization.sol"; @@ -27,27 +28,31 @@ contract ServiceAuthorizationV3 { constructor(string memory commitHash) - ServiceAuthorization(commitHash, 3) + ServiceAuthorization( + "ReleaseAdmin", + RELEASE(), + 3, + commitHash) {} function _setupDomains() internal override { - _authorizeDomain(REGISTRY(), address(1)); - _authorizeDomain(STAKING(), address(2)); - _authorizeDomain(INSTANCE(), address(3)); - _authorizeDomain(ACCOUNTING(), address(4)); - _authorizeDomain(COMPONENT(), address(5)); - _authorizeDomain(DISTRIBUTION(), address(6)); - _authorizeDomain(PRICE(), address(7)); - _authorizeDomain(BUNDLE(), address(8)); - _authorizeDomain(POOL(), address(9)); - _authorizeDomain(ORACLE(), address(10)); - _authorizeDomain(RISK(), address(11)); - _authorizeDomain(POLICY(), address(12)); - _authorizeDomain(CLAIM(), address(13)); - _authorizeDomain(APPLICATION(), address(14)); + _authorizeServiceDomain(REGISTRY(), address(1)); + _authorizeServiceDomain(STAKING(), address(2)); + _authorizeServiceDomain(INSTANCE(), address(3)); + _authorizeServiceDomain(ACCOUNTING(), address(4)); + _authorizeServiceDomain(COMPONENT(), address(5)); + _authorizeServiceDomain(DISTRIBUTION(), address(6)); + _authorizeServiceDomain(PRICE(), address(7)); + _authorizeServiceDomain(BUNDLE(), address(8)); + _authorizeServiceDomain(POOL(), address(9)); + _authorizeServiceDomain(ORACLE(), address(10)); + _authorizeServiceDomain(RISK(), address(11)); + _authorizeServiceDomain(POLICY(), address(12)); + _authorizeServiceDomain(CLAIM(), address(13)); + _authorizeServiceDomain(APPLICATION(), address(14)); } @@ -55,7 +60,7 @@ contract ServiceAuthorizationV3 internal override { - _setupIRegistryServiceAuthorization(); + _setupRegistryServiceAuthorization(); _setupStakingServiceAuthorization(); _setupInstanceServiceAuthorization(); _setupAccountingServiceAuthorization(); @@ -73,7 +78,7 @@ contract ServiceAuthorizationV3 /// @dev registry service authorization. /// authorized functions MUST be implemented with a restricted modifier - function _setupIRegistryServiceAuthorization() + function _setupRegistryServiceAuthorization() internal { IAccess.FunctionInfo[] storage functions; diff --git a/contracts/shared/ContractLib.sol b/contracts/shared/ContractLib.sol index 993c7f7ec..9d1f36097 100644 --- a/contracts/shared/ContractLib.sol +++ b/contracts/shared/ContractLib.sol @@ -6,8 +6,10 @@ import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessMana import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; // import {InstanceAdmin} from "../instance/InstanceAdmin.sol"; -import {IRegistry} from "../registry/IRegistry.sol"; import {IPolicyHolder} from "../shared/IPolicyHolder.sol"; +import {IRegistry} from "../registry/IRegistry.sol"; +import {IService} from "../shared/IService.sol"; + import {NftId} from "../type/NftId.sol"; import {ObjectType, PRODUCT, DISTRIBUTION, ORACLE, POOL, STAKING} from "../type/ObjectType.sol"; import {VersionPart} from "../type/Version.sol"; @@ -198,6 +200,15 @@ library ContractLib { } + function isService(address service) public view returns (bool) { + if (!isContract(service)) { + return false; + } + + return supportsInterface(service, type(IService).interfaceId); + } + + function isRegistry(address registry) public view returns (bool) { if (!isContract(registry)) { return false; diff --git a/contracts/shared/IService.sol b/contracts/shared/IService.sol index ab6d485b9..be13f5ebd 100644 --- a/contracts/shared/IService.sol +++ b/contracts/shared/IService.sol @@ -17,6 +17,7 @@ interface IService is /// In any GIF release only one service for any given domain may be deployed. function getDomain() external pure returns(ObjectType serviceDomain); + // TODO cleanup /// @dev returns the GIF release specific role id. /// These role ids are used to authorize service to service communication. function getRoleId() external view returns(RoleId serviceRoleId); diff --git a/contracts/shared/Service.sol b/contracts/shared/Service.sol index 175d8d3dd..1539a703b 100644 --- a/contracts/shared/Service.sol +++ b/contracts/shared/Service.sol @@ -7,7 +7,7 @@ import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/ut import {IRegistry} from "../registry/IRegistry.sol"; import {IService} from "./IService.sol"; import {IVersionable} from "../upgradeability/IVersionable.sol"; -import {ObjectType, REGISTRY, SERVICE} from "../type/ObjectType.sol"; +import {ObjectType, ObjectTypeLib, REGISTRY, SERVICE} from "../type/ObjectType.sol"; import {Registerable} from "./Registerable.sol"; import {RoleId, RoleIdLib} from "../type/RoleId.sol"; import {Version, VersionLib, VersionPartLib} from "../type/Version.sol"; @@ -53,8 +53,9 @@ abstract contract Service is return VersionLib.toVersion(3, 0, 0); } + // TODO cleanup function getRoleId() external virtual view returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(_getDomain(), getRelease()); + return RoleIdLib.toServiceRoleId(_getDomain(), getRelease()); } function _getDomain() internal virtual pure returns (ObjectType); diff --git a/contracts/type/ObjectType.sol b/contracts/type/ObjectType.sol index be528ea26..a2537a719 100644 --- a/contracts/type/ObjectType.sol +++ b/contracts/type/ObjectType.sol @@ -10,6 +10,7 @@ using { eqObjectType as ==, neObjectType as !=, ObjectTypeLib.toInt, + ObjectTypeLib.toName, ObjectTypeLib.eqz, ObjectTypeLib.eq, ObjectTypeLib.gtz @@ -143,7 +144,16 @@ function REFERRAL() pure returns (ObjectType) { return ObjectType.wrap(49); } +/// @dev Object type for GIF core target roles. +function CORE() pure returns (ObjectType) { + return ObjectType.wrap(97); +} +/// @dev Object type for target roles of contracts outside the GIF framework. +/// Example: Custom supporting contracts for a product component. +function CUSTOM() pure returns (ObjectType) { + return ObjectType.wrap(98); +} /// @dev Object type that includes any other object type. /// Note that eq()/'==' does not take this property into account. @@ -203,6 +213,8 @@ library ObjectTypeLib { return "Registry"; } else if (objectType == STAKING()) { return "Staking"; + } else if (objectType == RELEASE()) { + return "Release"; } else if (objectType == INSTANCE()) { return "Instance"; } else if (objectType == COMPONENT()) { @@ -239,6 +251,7 @@ library ObjectTypeLib { toInt(objectType)))); } + // TODO move to IService function toVersionedName( string memory name, string memory suffix, @@ -248,10 +261,10 @@ library ObjectTypeLib { pure returns (string memory versionedName) { - string memory versionName = "_v0"; + string memory versionName = "V0"; if (release.toInt() >= 10) { - versionName = "_v"; + versionName = "V"; } versionedName = string( diff --git a/contracts/type/RoleId.sol b/contracts/type/RoleId.sol index 6f0f09855..dc2a95719 100644 --- a/contracts/type/RoleId.sol +++ b/contracts/type/RoleId.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.20; import {Key32, KeyId, Key32Lib} from "./Key32.sol"; import {ObjectType, ROLE} from "./ObjectType.sol"; -import {VersionPart} from "./Version.sol"; +import {VersionPart, VersionPartLib} from "./Version.sol"; type RoleId is uint64; @@ -12,11 +12,9 @@ using { eqRoleId as ==, neRoleId as !=, RoleIdLib.toInt, + RoleIdLib.isServiceRole, RoleIdLib.eqz, - RoleIdLib.gtz, - RoleIdLib.isComponentRole, - RoleIdLib.isCustomRole - // RoleIdLib.toKey32 + RoleIdLib.gtz } for RoleId global; // general pure free functions @@ -56,12 +54,14 @@ function GIF_MANAGER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(2) /// @dev role for registering remote staking targets and reporting remote total value locked amounts. function GIF_REMOTE_MANAGER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(3); } +// TODO check if/where this is really needed /// @dev role assigned to release registry, release specfic to lock/unlock a release function RELEASE_REGISTRY_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(4); } /// @dev role assigned to every instance owner function INSTANCE_OWNER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(5); } +// TODO upate role id ranges //--- GIF core contract roles (range: 200 - 9'900) --------------------------// // created and assigned during initial deployment for registry and staking // granting for instances and components in instance service @@ -100,56 +100,72 @@ function CUSTOM_ROLE_MIN() pure returns (RoleId) { return RoleIdLib.toRoleId(100 library RoleIdLib { - uint64 public constant ALL_VERSIONS = 99; - uint64 public constant SERVICE_DOMAIN_ROLE_FACTOR = 100; - uint64 public constant COMPONENT_ROLE_FACTOR = 1000; - uint64 public constant COMPONENT_ROLE_MIN_INT = 12000; - uint64 public constant COMPONENT_ROLE_MAX_INT = 19000; - uint64 public constant CUSTOM_ROLE_MIN_INT = 1000000; + error ErrorRoleIdTooBig(uint256 roleId); + + // constant values need to match with AccessAdminLib.SERVICE_ROLE_* + uint64 public constant SERVICE_ROLE_MIN = 1000; + uint64 public constant SERVICE_ROLE_MAX = 99099; // 99 (max object type) * 1000 + 99 + + uint64 public constant SERVICE_ROLE_FACTOR = 1000; /// @dev Converts the RoleId to a uint. function zero() public pure returns (RoleId) { return RoleId.wrap(0); } + /// @dev Converts an uint into a role id. - function toRoleId(uint64 a) public pure returns (RoleId) { - return RoleId.wrap(a); - } + function toRoleId(uint256 a) public pure returns (RoleId) { + if (a > type(uint64).max) { + revert ErrorRoleIdTooBig(a); + } - /// @dev Converts an uint into a component role id. - function toComponentRoleId(ObjectType objectType, uint64 index) public pure returns (RoleId) { - return toRoleId(COMPONENT_ROLE_FACTOR * uint64(objectType.toInt()) + index); + return RoleId.wrap(uint64(a)); } - /// @dev Converts an uint into a custom role id. - function toCustomRoleId(uint64 index) public pure returns (RoleId) { - return toRoleId(CUSTOM_ROLE_MIN_INT + index); - } - /// @dev Converts the role id to a uint. - function toInt(RoleId a) public pure returns (uint64) { - return uint64(RoleId.unwrap(a)); + function isServiceRole(RoleId roleId) + public + pure + returns (bool) + { + uint256 roleIdInt = RoleId.unwrap(roleId); + return roleIdInt >= SERVICE_ROLE_MIN && roleIdInt <= SERVICE_ROLE_MAX; } - /// @dev Converts an uint into a role id. - /// Used for GIF core contracts. - function roleForType(ObjectType objectType) public pure returns (RoleId) { - return RoleId.wrap(SERVICE_DOMAIN_ROLE_FACTOR * uint64(objectType.toInt())); + + function toGenericServiceRoleId( + ObjectType objectType + ) + public + pure + returns (RoleId) + { + return toServiceRoleId( + objectType, + VersionPartLib.releaseMax()); } - /// @dev Converts an uint into a RoleId. - /// Used for GIF core contracts. - function roleForTypeAndVersion(ObjectType objectType, VersionPart majorVersion) public pure returns (RoleId) { - return RoleId.wrap( - uint64(SERVICE_DOMAIN_ROLE_FACTOR * objectType.toInt() + majorVersion.toInt())); + + function toServiceRoleId( + ObjectType serviceDomain, + VersionPart release + ) + public + pure + returns (RoleId serviceRoleId) + { + uint256 serviceRoleIdInt = + SERVICE_ROLE_MIN + + SERVICE_ROLE_FACTOR * (serviceDomain.toInt() - 1) + + release.toInt(); + + return toRoleId(serviceRoleIdInt); } - /// @dev Converts an uint into a RoleId. - /// Used for GIF core contracts. - function roleForTypeAndAllVersions(ObjectType objectType) public pure returns (RoleId) { - return RoleId.wrap( - uint64(SERVICE_DOMAIN_ROLE_FACTOR * objectType.toInt() + ALL_VERSIONS)); + /// @dev Converts the role id to a uint. + function toInt(RoleId a) public pure returns (uint64) { + return uint64(RoleId.unwrap(a)); } /// @dev Returns true if the value is non-zero (> 0). @@ -161,25 +177,4 @@ library RoleIdLib { function eqz(RoleId a) public pure returns (bool) { return RoleId.unwrap(a) == 0; } - - /// @dev Returns true iff the role id is a component role. - function isComponentRole(RoleId roleId) public pure returns (bool) { - uint64 roleIdInt = RoleId.unwrap(roleId); - return roleIdInt >= COMPONENT_ROLE_MIN_INT && roleIdInt <= COMPONENT_ROLE_MAX_INT; - } - - /// @dev Returns true iff the role id is a custom role. - function isCustomRole(RoleId roleId) public pure returns (bool) { - return RoleId.unwrap(roleId) >= CUSTOM_ROLE_MIN_INT; - } - - /// @dev Returns the key32 value for the specified id and object type. - function toKey32(RoleId a) public pure returns (Key32 key) { - return Key32Lib.toKey32(ROLE(), toKeyId(a)); - } - - /// @dev Returns the key id value for the specified id - function toKeyId(RoleId a) public pure returns (KeyId keyId) { - return KeyId.wrap(bytes31(uint248(RoleId.unwrap(a)))); - } } diff --git a/contracts/upgradeability/Versionable.sol b/contracts/upgradeability/Versionable.sol index 827b8fba2..1570c918c 100644 --- a/contracts/upgradeability/Versionable.sol +++ b/contracts/upgradeability/Versionable.sol @@ -39,7 +39,10 @@ abstract contract Versionable is // IMPORTANT each version must implement this function // each implementation MUST use onlyInitialising modifier // each implementation MUST call intializers of all base contracts... - function _initialize(address owner, bytes memory data) + function _initialize( + address, // owner + bytes memory // data + ) internal onlyInitializing() virtual diff --git a/scripts/libs/libraries.ts b/scripts/libs/libraries.ts index 5cb934a45..4bee869fc 100644 --- a/scripts/libs/libraries.ts +++ b/scripts/libs/libraries.ts @@ -105,7 +105,6 @@ export async function deployLibraries(owner: Signer): Promise { libraries: { ObjectTypeLib: objectTypeLibAddress, - Key32Lib: key32LibAddress, VersionPartLib: versionPartLibAddress, } }); @@ -362,6 +361,7 @@ export async function deployLibraries(owner: Signer): Promise SelectorLib: selectorLibAddress, StrLib: strLibAddress, TimestampLib: timestampLibAddress, + VersionPartLib: versionPartLibAddress, } }); LIBRARY_ADDRESSES.set("AccessAdminLib", accessAdminLibAddress); diff --git a/scripts/libs/registry.ts b/scripts/libs/registry.ts index f061f5177..aea8f5def 100644 --- a/scripts/libs/registry.ts +++ b/scripts/libs/registry.ts @@ -65,10 +65,13 @@ export type RegistryAddresses = { } export async function deployAndInitializeRegistry(owner: Signer, libraries: LibraryAddresses): Promise { + logger.info("======== Starting deployment of registry ========"); logger.info("-------- Starting deployment DIP ----------------"); + const COMMIT_HASH = "1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a"; + const { address: dipAddress, contract: dipBaseContract } = await deployContract( "Dip", owner, // GIF_ADMIN_ROLE @@ -87,6 +90,7 @@ export async function deployAndInitializeRegistry(owner: Signer, libraries: Libr "RegistryAuthorization", owner, [ + COMMIT_HASH, ], { libraries: { @@ -321,6 +325,8 @@ export async function deployAndInitializeRegistry(owner: Signer, libraries: Libr [ "a41a84af9a430ef22e00d9c4a8012ce24830e7bf" ], { libraries: { + ObjectTypeLib: libraries.objectTypeLibAddress, + RoleIdLib: libraries.roleIdLibAddress, SelectorLib: libraries.selectorLibAddress, StrLib: libraries.strLibAddress, TimestampLib: libraries.timestampLibAddress, diff --git a/test/authorization/AccessAdmin.t.sol b/test/authorization/AccessAdmin.t.sol index ca1932491..ae0a095d0 100644 --- a/test/authorization/AccessAdmin.t.sol +++ b/test/authorization/AccessAdmin.t.sol @@ -8,7 +8,7 @@ import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import {Test, console} from "../../lib/forge-std/src/Test.sol"; -import {AccessAdmin} from "../../contracts/authorization/AccessAdmin.sol"; +import {AccessAdmin, ADMIN_ROLE_NAME, PUBLIC_ROLE_NAME} from "../../contracts/authorization/AccessAdmin.sol"; import {AccessAdminLib} from "../../contracts/authorization/AccessAdminLib.sol"; import {AccessManagerCloneable} from "../../contracts/authorization/AccessManagerCloneable.sol"; import {AccessManagedMock} from "../mock/AccessManagedMock.sol"; @@ -51,7 +51,7 @@ contract AccessAdminForTesting is AccessAdmin { release); // create targets for testing - _createTarget(address(this), "AccessAdmin", false, true); + _createUncheckedTarget(address(this), "AccessAdmin", IAccess.TargetType.Core); // setup manager role _managerRoleId = RoleIdLib.toRoleId(MANAGER_ROLE); @@ -173,9 +173,10 @@ contract AccessAdminForTesting is AccessAdmin { virtual restricted() { + bool createRole = true; + bool customTarget = true; bool checkAuthoritiy = true; - bool custom = true; - _createTarget(target, name, checkAuthoritiy, custom); + _createManagedTarget(target, name, IAccess.TargetType.Custom); } function authorizeFunctions( @@ -413,7 +414,7 @@ contract AccessAdminTest is AccessAdminBaseTest { function test_accessAdminCreateRoleHappyCase() public { // GIVEN (just setup) - RoleId newRoleId = RoleIdLib.toRoleId(100); + RoleId newRoleId = RoleIdLib.toRoleId(123456789); RoleId adminRoleId = accessAdmin.getManagerRole(); string memory newRoleName = "NewRole"; @@ -447,7 +448,7 @@ contract AccessAdminTest is AccessAdminBaseTest { function test_accessAdminCreateRoleWithSingleMember() public { // GIVEN (just setup) - RoleId newRoleId = RoleIdLib.toRoleId(100); + RoleId newRoleId = RoleIdLib.toRoleId(123456789); RoleId adminRoleId = accessAdmin.getManagerRole(); string memory newRoleName = "NewRole"; @@ -566,7 +567,7 @@ contract AccessAdminTest is AccessAdminBaseTest { function test_accessAdminCreateRoleTwice() public { // GIVEN - RoleId newRoleId = RoleIdLib.toRoleId(100); + RoleId newRoleId = RoleIdLib.toRoleId(123456789); RoleId adminRoleId = accessAdmin.getManagerRole(); string memory newRoleName = "NewRole"; @@ -611,7 +612,7 @@ contract AccessAdminTest is AccessAdminBaseTest { function test_accessAdminCreateEmptyNameMissingAdminRole() public { // GIVEN - RoleId newRoleId = RoleIdLib.toRoleId(100); + RoleId newRoleId = RoleIdLib.toRoleId(123456789); RoleId missingAdminRoleId = RoleIdLib.toRoleId(111); RoleId adminRoleId = accessAdmin.getManagerRole(); string memory newRoleName = "NewRole"; @@ -652,7 +653,7 @@ contract AccessAdminTest is AccessAdminBaseTest { address account2 = makeAddr("account2"); RoleId adminRoleId = accessAdmin.getManagerRole(); - RoleId newAdminRoleId = RoleIdLib.toRoleId(100); + RoleId newAdminRoleId = RoleIdLib.toRoleId(123456789); string memory newRoleAdminName = "NewRoleAdmin"; RoleId newRoleId = RoleIdLib.toRoleId(101); @@ -826,7 +827,7 @@ contract AccessAdminTest is AccessAdminBaseTest { function test_accessAdminCreateRoleUnauthorized() public { // GIVEN (just setup) - RoleId newRoleId = RoleIdLib.toRoleId(100); + RoleId newRoleId = RoleIdLib.toRoleId(123456789); RoleId adminRoleId = accessAdmin.getAdminRole(); string memory newRoleName = "NewRole"; @@ -1071,20 +1072,20 @@ contract AccessAdminTest is AccessAdminBaseTest { assertTrue(aa.isRoleMember(aa.getPublicRole(), outsider), "outsider missing public role"); // count roles and check role ids - assertEq(aa.roles(), 3, "unexpected number of roles for freshly initialized access admin"); + assertEq(aa.roles(), 4, "unexpected number of roles for freshly initialized access admin"); assertEq(aa.getRoleId(0).toInt(), aa.getAdminRole().toInt(), "unexpected admin role id"); assertEq(aa.getRoleId(0).toInt(), type(uint64).min, "unexpected admin role id (absolute)"); assertEq(aa.getRoleId(1).toInt(), aa.getPublicRole().toInt(), "unexpected public role id"); assertEq(aa.getRoleId(1).toInt(), type(uint64).max, "unexpected public role id (absolute)"); - assertEq(aa.getRoleId(2).toInt(), aa.getManagerRole().toInt(), "unexpected manager role id"); - assertEq(aa.getRoleId(2).toInt(), 1, "unexpected manager role id (absolute)"); + assertEq(aa.getRoleId(3).toInt(), aa.getManagerRole().toInt(), "unexpected manager role id"); + assertEq(aa.getRoleId(3).toInt(), 1, "unexpected manager role id (absolute)"); // check admin role _checkRole( aa, aa.getAdminRole(), aa.getAdminRole(), - aa.ADMIN_ROLE_NAME(), + ADMIN_ROLE_NAME(), 1, // only one admin ! (aa contract is sole admin role member) TimestampLib.blockTimestamp()); @@ -1093,7 +1094,7 @@ contract AccessAdminTest is AccessAdminBaseTest { aa, aa.getPublicRole(), aa.getAdminRole(), - aa.PUBLIC_ROLE_NAME(), + PUBLIC_ROLE_NAME(), type(uint32).max, // every account is public role member TimestampLib.blockTimestamp()); diff --git a/test/authorization/AccessAdminManageMock.t.sol b/test/authorization/AccessAdminManageMock.t.sol index cf205c164..acdb6ddf1 100644 --- a/test/authorization/AccessAdminManageMock.t.sol +++ b/test/authorization/AccessAdminManageMock.t.sol @@ -8,7 +8,7 @@ import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import {Test, console} from "../../lib/forge-std/src/Test.sol"; -import {AccessAdmin} from "../../contracts/authorization/AccessAdmin.sol"; +import {AccessAdmin, ADMIN_ROLE_NAME, PUBLIC_ROLE_NAME} from "../../contracts/authorization/AccessAdmin.sol"; import {AccessAdminLib} from "../../contracts/authorization/AccessAdminLib.sol"; import {AccessAdminForTesting} from "./AccessAdmin.t.sol"; import {AccessManagedMock} from "../mock/AccessManagedMock.sol"; @@ -327,20 +327,20 @@ contract AccessAdminManageMockTest is AccessAdminBaseTest { assertTrue(aa.isRoleMember(aa.getPublicRole(), outsider), "outsider missing public role"); // count roles and check role ids - assertEq(aa.roles(), 3, "unexpected number of roles for freshly initialized access admin"); + assertEq(aa.roles(), 5, "unexpected number of roles for freshly initialized access admin"); assertEq(aa.getRoleId(0).toInt(), aa.getAdminRole().toInt(), "unexpected admin role id"); assertEq(aa.getRoleId(0).toInt(), type(uint64).min, "unexpected admin role id (absolute)"); assertEq(aa.getRoleId(1).toInt(), aa.getPublicRole().toInt(), "unexpected public role id"); assertEq(aa.getRoleId(1).toInt(), type(uint64).max, "unexpected public role id (absolute)"); - assertEq(aa.getRoleId(2).toInt(), aa.getManagerRole().toInt(), "unexpected manager role id"); - assertEq(aa.getRoleId(2).toInt(), 1, "unexpected manager role id (absolute)"); + assertEq(aa.getRoleId(3).toInt(), aa.getManagerRole().toInt(), "unexpected manager role id"); + assertEq(aa.getRoleId(3).toInt(), 1, "unexpected manager role id (absolute)"); // check admin role _checkRole( aa, aa.getAdminRole(), aa.getAdminRole(), - aa.ADMIN_ROLE_NAME(), + ADMIN_ROLE_NAME(), TimestampLib.max(), TimestampLib.blockTimestamp()); @@ -349,7 +349,7 @@ contract AccessAdminManageMockTest is AccessAdminBaseTest { aa, aa.getPublicRole(), aa.getAdminRole(), - aa.PUBLIC_ROLE_NAME(), + PUBLIC_ROLE_NAME(), TimestampLib.max(), TimestampLib.blockTimestamp()); diff --git a/test/authorization/RegistryAdminEx.sol b/test/authorization/RegistryAdminEx.sol index 764feb48a..66be31a00 100644 --- a/test/authorization/RegistryAdminEx.sol +++ b/test/authorization/RegistryAdminEx.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IAccess} from "../../contracts/authorization/IAccess.sol"; import {IAuthorization} from "../../contracts/authorization/IAuthorization.sol"; import {IRegistry} from "../../contracts/registry/IRegistry.sol"; @@ -32,7 +33,10 @@ contract RegistryAdminEx is RegistryAdmin { accessManagedMock = new AccessManagedMock(address(authority())); // create target for access managed mock - _createTarget(address(accessManagedMock), "AccessManagedMock", false, false); + _createUncheckedTarget( + address(accessManagedMock), + "AccessManagedMock", + IAccess.TargetType.Component); // check authority // grant permissions to public role for access managed mock FunctionInfo[] memory functions; diff --git a/test/base/Authz.t.sol b/test/base/Authz.t.sol new file mode 100644 index 000000000..6f4bb0fc2 --- /dev/null +++ b/test/base/Authz.t.sol @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {Test, console} from "../../lib/forge-std/src/Test.sol"; + +import {IAccess} from "../../contracts/authorization/IAccess.sol"; +import {IAuthorization} from "../../contracts/authorization/IAuthorization.sol"; +import {IServiceAuthorization} from "../../contracts/authorization/IServiceAuthorization.sol"; + +import {InstanceAuthorizationV3} from "../../contracts/instance/InstanceAuthorizationV3.sol"; +import {ObjectType} from "../../contracts/type/ObjectType.sol"; +import {RoleId} from "../../contracts/type/RoleId.sol"; +import {RegistryAuthorization} from "../../contracts/registry/RegistryAuthorization.sol"; +import {SimpleDistributionAuthorization} from "../../contracts/examples/unpermissioned/SimpleDistributionAuthorization.sol"; +import {BasicOracleAuthorization} from "../../contracts/oracle/BasicOracleAuthorization.sol"; +import {SimplePoolAuthorization} from "../../contracts/examples/unpermissioned/SimplePoolAuthorization.sol"; +import {SimpleProductAuthorization} from "../../contracts/examples/unpermissioned/SimpleProductAuthorization.sol"; +import {ServiceAuthorizationV3} from "../../contracts/registry/ServiceAuthorizationV3.sol"; +import {Str} from "../../contracts/type/String.sol"; + + +contract AuthorizationTest is Test { + + string public constant COMMIT_HASH = "1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a"; + + RegistryAuthorization public registryAuthz; + IAuthorization public iRegistryAuthz; + + ServiceAuthorizationV3 public serviceAuthz; + IServiceAuthorization public iServiceAuthz; + + InstanceAuthorizationV3 public instanceAuthz; + IAuthorization public iInstanceAuthz; + + SimpleProductAuthorization public prodAuthz; + IAuthorization public iProdAuthz; + + SimpleDistributionAuthorization public distAuthz; + IAuthorization public iDistAuthz; + + BasicOracleAuthorization public oracleAuthz; + IAuthorization public iOracleAuthz; + + SimplePoolAuthorization public poolAuthz; + IAuthorization public iPoolAuthz; + + function setUp() public { + // registry authorization + registryAuthz = new RegistryAuthorization(COMMIT_HASH); + iRegistryAuthz = IAuthorization(address(registryAuthz)); + + // service authorization + serviceAuthz = new ServiceAuthorizationV3(COMMIT_HASH); + iServiceAuthz = IServiceAuthorization(address(serviceAuthz)); + + // instance authorization + instanceAuthz = new InstanceAuthorizationV3(); + iInstanceAuthz = IAuthorization(address(instanceAuthz)); + + // product authorization + prodAuthz = new SimpleProductAuthorization("SimpleProduct"); + iProdAuthz = IAuthorization(address(prodAuthz)); + + // distribution authorization + distAuthz = new SimpleDistributionAuthorization("SimpleDistribution"); + iDistAuthz = IAuthorization(address(distAuthz)); + + // oracle authorization + oracleAuthz = new BasicOracleAuthorization("SimpleOracle", COMMIT_HASH); + iOracleAuthz = IAuthorization(address(oracleAuthz)); + + // pool authorization + poolAuthz = new SimplePoolAuthorization("SimplePool"); + iPoolAuthz = IAuthorization(address(poolAuthz)); + } + + + function test_authorizationSetupRegistryAuthz() public view { _printAuthz(iRegistryAuthz); _checkAuthz(iRegistryAuthz); } + function test_authorizationSetupServiceAuthz() public view { _printServiceAuthz(iServiceAuthz); _checkServiceAuthz(iServiceAuthz); } + + function test_authorizationSetupInstanceAuthz() public view { _printAuthz(iInstanceAuthz); _checkAuthz(iInstanceAuthz); } + function test_authorizationSetupProductAuthz() public view { _printAuthz(iProdAuthz); _checkAuthz(iProdAuthz); } + function test_authorizationSetupDistributionAuthz() public view { _printAuthz(iDistAuthz); _checkAuthz(iDistAuthz); } + function test_authorizationSetupOracleAuthz() public view { _printAuthz(iOracleAuthz); _checkAuthz(iOracleAuthz); } + function test_authorizationSetupPoolAuthz() public view { _printAuthz(iPoolAuthz); _checkAuthz(iPoolAuthz); } + + + function _checkServiceAuthz(IServiceAuthorization iAuthz) internal view { + ObjectType[] memory serviceDomains = iAuthz.getServiceDomains(); + + for(uint256 i = 0; i < serviceDomains.length; i++) { + Str target = iAuthz.getServiceTarget(serviceDomains[i]); + _checkTargetAuthz(address(iAuthz), target); + } + } + + + function _checkAuthz(IAuthorization iAuthz) internal view { + Str[] memory targets = iAuthz.getTargets(); + + for(uint256 i = 0; i < targets.length; i++) { + Str target = targets[i]; + _checkTargetAuthz(address(iAuthz), target); + } + } + + + function _checkTargetAuthz(address iAuthzAddress, Str target) internal view { + IAuthorization iAuthz = IAuthorization(iAuthzAddress); + RoleId [] memory roleIds = iAuthz.getAuthorizedRoles(target); + + for (uint256 j = 0; j < roleIds.length; j++) { + RoleId roleId = roleIds[j]; + + if (roleId.eqz()) { console.log(target.toString(), "role", j); } + assertTrue(roleId.gtz(), "role id zero"); + } + } + + + function _printAuthz(IAuthorization iAuthz) internal view { + // solhint-disable + console.log("---", iAuthz.getMainTargetName() ,"--------------"); + console.log("main target name (via target):", iAuthz.getMainTarget().toString()); + console.log("main target role:", iAuthz.getTargetRole(iAuthz.getMainTarget()).toInt()); + // solhint-enable + + _printServiceDomains(address(iAuthz), false); + _printRoles(address(iAuthz)); + _printTargets(iAuthz); + _printFunctions(iAuthz); + } + + + function _printServiceAuthz(IServiceAuthorization iAuthz) internal view { + // solhint-disable + console.log("---", iAuthz.getMainTargetName() ,"--------------"); + console.log("main target name (via target):", iAuthz.getMainTarget().toString()); + console.log("main target role:", iAuthz.getTargetRole(iAuthz.getMainTarget()).toInt()); + // solhint-enable + + _printServiceDomains(address(iAuthz), true); + _printRoles(address(iAuthz)); + } + + + function _printRoles(address addreiAuthzAddress) internal view { + IAuthorization iAuthz = IAuthorization(addreiAuthzAddress); + RoleId[] memory roles = iAuthz.getRoles(); + + // solhint-disable next-line + console.log("--- roles", roles.length, "----------"); + + for(uint256 i = 0; i < roles.length; i++) { + RoleId roleId = roles[i]; + string memory roleName = iAuthz.getRoleInfo(roleId).name.toString(); + + // solhint-disable next-line + console.log("role:", roleId.toInt(), roleName); + } + } + + function _printTargets(IAuthorization iAuthz) internal view { + Str[] memory targets = iAuthz.getTargets(); + + // solhint-disable next-line + console.log("--- targets", targets.length, "----------"); + + for(uint256 i = 0; i < targets.length; i++) { + Str target = targets[i]; + RoleId roleId = iAuthz.getTargetRole(target); + + // solhint-disable + if (roleId.gtz()) { + string memory roleName = iAuthz.getRoleInfo(roleId).name.toString(); + console.log("target:", target.toString(), roleName, roleId.toInt()); + } else { + // solhint-disable next-line + console.log("target:", target.toString(), ""); + } + // solhint-enable + } + } + + function _printFunctions(IAuthorization iAuthz) internal view { + // solhint-disable next-line + console.log("--- functions -----------------------"); + + Str[] memory targets = iAuthz.getTargets(); + for(uint256 i = 0; i < targets.length; i++) { + Str target = targets[i]; + + RoleId [] memory roleIds = iAuthz.getAuthorizedRoles(target); + if (roleIds.length > 0) { + // solhint-disable next-line + console.log("target:", target.toString()); + + for(uint256 j = 0; j < roleIds.length; j++) { + _printRoleGrantings(iAuthz, target, roleIds[j]); + } + } + } + } + + function _printServiceDomains(address authz, bool printFunctions) internal view { + IAuthorization iAuthz = IAuthorization(authz); + ObjectType[] memory serviceDomains = iAuthz.getServiceDomains(); + + // solhint-disable next-line + console.log("--- service domains", serviceDomains.length, "----------"); + + for(uint256 i = 0; i < serviceDomains.length; i++) { + ObjectType serviceDomain = iAuthz.getServiceDomain(i); + string memory targetName = iAuthz.getServiceTarget(serviceDomain).toString(); + RoleId targetRole = iAuthz.getServiceRole(serviceDomain); + string memory roleName = iAuthz.getRoleInfo(targetRole).name.toString(); + + // solhint-disable + console.log("domain:", serviceDomain.toInt(), serviceDomain.toName()); + if (printFunctions) { + console.log(" target:", targetName, iAuthz.getServiceAddress(serviceDomain)); + console.log(" role:", targetRole.toInt(), roleName); + } + // solhint-enable + } + + if (printFunctions) { + // solhint-disable next-line + console.log("--- authorized functions --------------"); + for(uint256 i = 0; i < serviceDomains.length; i++) { + ObjectType serviceDomain = iAuthz.getServiceDomain(i); + Str target = iAuthz.getServiceTarget(serviceDomain); + RoleId[] memory roleIds = iAuthz.getAuthorizedRoles(target); + + // solhint-disable next-line + console.log(target.toString(), "domain", serviceDomain.toInt()); + + for(uint256 j = 0; j < roleIds.length; j++) { + _printRoleGrantings(iAuthz, target, roleIds[j]); + } + } + } + } + + function _printRoleGrantings(IAuthorization iAuthz, Str target, RoleId roleId) internal view { + string memory roleName = iAuthz.getRoleInfo(roleId).name.toString(); + + // solhint-disable next-line + console.log(" ", roleName, roleId.toInt()); + + IAccess.FunctionInfo[] memory functions = iAuthz.getAuthorizedFunctions(target, roleId); + for(uint256 k = 0; k < functions.length; k++) { + string memory functionName = string(abi.encodePacked( + " - ", functions[k].name.toString(), "()")); + + // solhint-disable next-line + console.log(functionName); + } + } +} \ No newline at end of file diff --git a/test/TestDeployAll.t.sol b/test/base/DeployAll.t.sol similarity index 83% rename from test/TestDeployAll.t.sol rename to test/base/DeployAll.t.sol index 8bd441ff9..49c2be9e2 100644 --- a/test/TestDeployAll.t.sol +++ b/test/base/DeployAll.t.sol @@ -2,15 +2,15 @@ pragma solidity ^0.8.20; -import {GifTest} from "./base/GifTest.sol"; -import {InstanceLinkedComponent} from "../contracts/shared/InstanceLinkedComponent.sol"; -import {IRegistry} from "../contracts/registry/IRegistry.sol"; -import {NftId, NftIdLib} from "../contracts/type/NftId.sol"; -import {ObjectType} from "../contracts/type/ObjectType.sol"; -import {BUNDLE, COMPONENT, DISTRIBUTION, ORACLE, POOL, PRODUCT, POLICY, RISK, REQUEST, SERVICE, STAKING} from "../contracts/type/ObjectType.sol"; -import {RoleId} from "../contracts/type/RoleId.sol"; +import {GifTest} from "./GifTest.sol"; +import {InstanceLinkedComponent} from "../../contracts/shared/InstanceLinkedComponent.sol"; +import {IRegistry} from "../../contracts/registry/IRegistry.sol"; +import {NftId, NftIdLib} from "../../contracts/type/NftId.sol"; +import {ObjectType} from "../../contracts/type/ObjectType.sol"; +import {BUNDLE, COMPONENT, DISTRIBUTION, ORACLE, POOL, PRODUCT, POLICY, RISK, REQUEST, SERVICE, STAKING} from "../../contracts/type/ObjectType.sol"; +import {RoleId} from "../../contracts/type/RoleId.sol"; -contract TestDeployAll is GifTest { +contract DeployAllTest is GifTest { function setUp() public override { super.setUp(); @@ -22,36 +22,6 @@ contract TestDeployAll is GifTest { assertTrue(true); } - // function _getTargetText(uint256 idx) internal returns (string memory) { - // address target = registryAdmin.getTargetAddress(idx); - // return string( - // abi.encodePacked( - // "address ", - // _toString(target), - // " ", - // registryAdmin.getTargetInfo(target).name)); - // } - - // function _getRoleText(uint256 idx) internal returns (string memory) { - // RoleId roleId = registryAdmin.getRoleId(idx); - // return string( - // abi.encodePacked( - // "roleId ", - // _toString(roleId.toInt()), - // " ", - // registryAdmin.getRoleInfo(roleId).name, - // " members ", - // _toString(registryAdmin.roleMembers(roleId)))); - // } - - // function _toString(uint256 value) internal pure returns (string memory) { - // return Strings.toString(value); - // } - - // function _toString(address _address) internal pure returns (string memory) { - // return Strings.toHexString(uint256(uint160(_address)), 20); - // } - function test_deploySimpleProduct() public { _checkMockComponent(product, productNftId, instanceNftId, PRODUCT(), "SimpleProduct", productOwner); } diff --git a/test/base/GifClusterTest.sol b/test/base/GifClusterTest.sol index bf4307f21..d0c2e6082 100644 --- a/test/base/GifClusterTest.sol +++ b/test/base/GifClusterTest.sol @@ -213,7 +213,7 @@ contract GifClusterTest is GifTest { return new SimpleOracle( address(registry), productNftId, - new BasicOracleAuthorization(name), + new BasicOracleAuthorization(name, COMMIT_HASH), owner); } diff --git a/test/base/GifDeployer.sol b/test/base/GifDeployer.sol index b9b4846df..4dd4f559e 100644 --- a/test/base/GifDeployer.sol +++ b/test/base/GifDeployer.sol @@ -5,6 +5,7 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER import {AccessManager} from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import {Test, console} from "../../lib/forge-std/src/Test.sol"; +import {IAccess} from "../../contracts/authorization/IAccess.sol"; import {IAccessAdmin} from "../../contracts/authorization/IAccessAdmin.sol"; import {IAuthorization} from "../../contracts/authorization/IAuthorization.sol"; import {IRegistry} from "../../contracts/registry/IRegistry.sol"; @@ -13,15 +14,16 @@ import {IServiceAuthorization} from "../../contracts/authorization/IServiceAutho import {AmountLib} from "../../contracts/type/Amount.sol"; import {ObjectType, ObjectTypeLib} from "../../contracts/type/ObjectType.sol"; +import {ChainNft} from "../../contracts/registry/ChainNft.sol"; import {NftId, NftIdLib} from "../../contracts/type/NftId.sol"; import {ProxyManager} from "../../contracts/upgradeability/ProxyManager.sol"; import {SCHEDULED, DEPLOYING} from "../../contracts/type/StateId.sol"; import {VersionPart, VersionPartLib} from "../../contracts/type/Version.sol"; import {RegistryAuthorization} from "../../contracts/registry/RegistryAuthorization.sol"; +import {RoleId} from "../../contracts/type/RoleId.sol"; import {StateIdLib} from "../../contracts/type/StateId.sol"; import {TimestampLib} from "../../contracts/type/Timestamp.sol"; - // core contracts import {Dip} from "../../contracts/mock/Dip.sol"; import {Registry} from "../../contracts/registry/Registry.sol"; @@ -67,12 +69,37 @@ import {StakingServiceManager} from "../../contracts/staking/StakingServiceManag contract GifDeployer is Test { + string public constant COMMIT_HASH = "1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a"; + struct DeployedServiceInfo { NftId nftId; address service; address proxy; } + // global accounts + address public globalRegistry = makeAddr("globalRegistry"); + address public registryOwner = makeAddr("registryOwner"); + address public stakingOwner = registryOwner; + address public gifAdmin = registryOwner; + address public gifManager = registryOwner; + + // deploy core + IERC20Metadata public dip; + Registry public registry; + TokenRegistry public tokenRegistry; + ReleaseRegistry public releaseRegistry; + RegistryAdmin public registryAdmin; + StakingManager public stakingManager; + Staking public staking; + + address public registryAddress; + ChainNft public chainNft; + NftId public registryNftId; + NftId public stakingNftId; + StakingReader public stakingReader; + + // deploy release services RegistryServiceManager public registryServiceManager; RegistryService public registryService; NftId public registryServiceNftId; @@ -131,6 +158,7 @@ contract GifDeployer is Test { mapping(ObjectType domain => DeployedServiceInfo info) public serviceForDomain; + function deployCore( address globalRegistry, address gifAdmin, @@ -196,7 +224,7 @@ contract GifDeployer is Test { // 11) initialize registry admin registryAdmin.completeSetup( address(registry), - address(new RegistryAuthorization()), + address(new RegistryAuthorization(COMMIT_HASH)), gifAdmin, gifManager); @@ -207,22 +235,22 @@ contract GifDeployer is Test { function deployRelease( ReleaseRegistry releaseRegistry, IServiceAuthorization serviceAuthorization, - address gifAdmin, - address gifManager + address admin, + address manager ) public { - vm.startPrank(gifAdmin); + vm.startPrank(admin); releaseRegistry.createNextRelease(); vm.stopPrank(); - vm.startPrank(gifManager); + vm.startPrank(manager); _deployReleaseServices( releaseRegistry, serviceAuthorization); vm.stopPrank(); - vm.startPrank(gifAdmin); + vm.startPrank(admin); releaseRegistry.activateNextRelease(); vm.stopPrank(); } @@ -474,4 +502,142 @@ contract GifDeployer is Test { }) ); } + + + function _deployCore( + address gifAdmin, + address gifManager + ) + internal + { + ( + dip, + registry, + tokenRegistry, + releaseRegistry, + registryAdmin, + stakingManager, + staking + ) = deployCore( + globalRegistry, + gifAdmin, + gifManager, + stakingOwner); + + // obtain some references + registryAddress = address(registry); + chainNft = ChainNft(registry.getChainNftAddress()); + registryNftId = registry.getNftIdForAddress(registryAddress); + stakingNftId = registry.getNftIdForAddress(address(staking)); + stakingReader = staking.getStakingReader(); + } + + + function _printCoreSetup() internal view { + // solhint-disable + console.log("registry deployed at", address(registry)); + console.log("registry owner", registryOwner); + + console.log("token registry deployed at", address(tokenRegistry)); + console.log("release manager deployed at", address(releaseRegistry)); + + console.log("registry access manager deployed:", address(registryAdmin)); + console.log("registry access manager authority", registryAdmin.authority()); + + console.log("staking manager deployed at", address(stakingManager)); + + console.log("staking nft id", registry.getNftIdForAddress(address(staking)).toInt()); + console.log("staking deployed at", address(staking)); + console.log("staking owner (opt 1)", registry.ownerOf(address(staking))); + console.log("staking owner (opt 2)", staking.getOwner()); + // solhint-enable + } + + + function _printAuthz( + IAccessAdmin aa, + string memory aaName + ) + internal + { + // solhint-disable no-console + console.log("=========================================="); + console.log(aaName, registry.getObjectAddress(aa.getLinkedNftId())); + console.log(aaName, "nft id", aa.getLinkedNftId().toInt()); + console.log(aaName, "owner", aa.getLinkedOwner()); + console.log(aaName, "admin authorization"); + console.log(aaName, "admin contract:", address(aa)); + console.log(aaName, "admin authority:", aa.authority()); + + uint256 roles = aa.roles(); + uint256 targets = aa.targets(); + + console.log("------------------------------------------"); + console.log("roles", aa.roles()); + // solhint-enable + + for(uint256 i = 0; i < aa.roles(); i++) { + _printRoleMembers(aa, aa.getRoleId(i)); + } + + // solhint-disable no-console + console.log("------------------------------------------"); + console.log("targets", aa.targets()); + // solhint-enable + + for(uint256 i = 0; i < aa.targets(); i++) { + _printTarget(aa, aa.getTargetAddress(i)); + } + } + + + function _printRoleMembers(IAccessAdmin aa, RoleId roleId) internal { + IAccessAdmin.RoleInfo memory info = aa.getRoleInfo(roleId); + uint256 members = aa.roleMembers(roleId); + + // solhint-disable no-console + console.log("role", info.name.toString(), "id", roleId.toInt()); + + if (members > 0) { + for(uint i = 0; i < members; i++) { + address memberAddress = aa.getRoleMember(roleId, i); + string memory targetName = "(not target)"; + if (aa.targetExists(memberAddress)) { + targetName = aa.getTargetInfo(memberAddress).name.toString(); + } + + console.log("-", i, aa.getRoleMember(roleId, i), targetName); + } + } else { + console.log("- no role members"); + } + + console.log(""); + // solhint-enable + } + + function _printTarget(IAccessAdmin aa, address target) internal view { + IAccessAdmin.TargetInfo memory info = aa.getTargetInfo(target); + + // solhint-disable no-console + uint256 functions = aa.authorizedFunctions(target); + console.log("target", info.name.toString(), "address", target); + + if (functions > 0) { + for(uint256 i = 0; i < functions; i++) { + ( + IAccess.FunctionInfo memory func, + RoleId roleId + ) = aa.getAuthorizedFunction(target, i); + string memory role = aa.getRoleInfo(roleId).name.toString(); + + console.log("-", i, string(abi.encodePacked(func.name.toString(), "(): role ", role,":")), roleId.toInt()); + } + } else { + console.log("- no authorized functions"); + } + + console.log(""); + // solhint-enable + } } \ No newline at end of file diff --git a/test/GifDeployer.t.sol b/test/base/GifDeployer.t.sol similarity index 71% rename from test/GifDeployer.t.sol rename to test/base/GifDeployer.t.sol index 02cfaf690..6b929ccf7 100644 --- a/test/GifDeployer.t.sol +++ b/test/base/GifDeployer.t.sol @@ -3,57 +3,79 @@ pragma solidity ^0.8.20; import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import {console} from "../lib/forge-std/src/Test.sol"; - -import {ChainNft} from "../contracts/registry/ChainNft.sol"; -import {Dip} from "../contracts/mock/Dip.sol"; -import {GifDeployer} from "./base/GifDeployer.sol"; -import {GIF_MANAGER_ROLE, GIF_ADMIN_ROLE} from "../contracts/type/RoleId.sol"; -import {IRegistry} from "../contracts/registry/IRegistry.sol"; -import {IServiceAuthorization} from "../contracts/authorization/IServiceAuthorization.sol"; -import {NftId, NftIdLib} from "../contracts/type/NftId.sol"; -import {Registry} from "../contracts/registry/Registry.sol"; -import {RegistryAdmin} from "../contracts/registry/RegistryAdmin.sol"; -import {ReleaseRegistry} from "../contracts/registry/ReleaseRegistry.sol"; -import {REGISTRY, STAKING} from "../contracts/type/ObjectType.sol"; -import {Selector, SelectorLib} from "../contracts/type/Selector.sol"; -import {ServiceAuthorizationV3} from "../contracts/registry/ServiceAuthorizationV3.sol"; -import {Staking} from "../contracts/staking/Staking.sol"; -import {StakingManager} from "../contracts/staking/StakingManager.sol"; -import {StakingReader} from "../contracts/staking/StakingReader.sol"; -import {StakingStore} from "../contracts/staking/StakingStore.sol"; -import {TokenRegistry} from "../contracts/registry/TokenRegistry.sol"; -import {VersionPart, VersionPartLib} from "../contracts/type/Version.sol"; +import {console} from "../../lib/forge-std/src/Test.sol"; + +import {ChainNft} from "../../contracts/registry/ChainNft.sol"; +import {Dip} from "../../contracts/mock/Dip.sol"; +import {GifDeployer} from "./GifDeployer.sol"; +import {GIF_MANAGER_ROLE, GIF_ADMIN_ROLE} from "../../contracts/type/RoleId.sol"; +import {IRegistry} from "../../contracts/registry/IRegistry.sol"; +import {IServiceAuthorization} from "../../contracts/authorization/IServiceAuthorization.sol"; +import {NftId, NftIdLib} from "../../contracts/type/NftId.sol"; +import {Registry} from "../../contracts/registry/Registry.sol"; +import {RegistryAdmin} from "../../contracts/registry/RegistryAdmin.sol"; +import {ReleaseAdmin} from "../../contracts/registry/ReleaseAdmin.sol"; +import {ReleaseRegistry} from "../../contracts/registry/ReleaseRegistry.sol"; +import {REGISTRY, STAKING} from "../../contracts/type/ObjectType.sol"; +import {Selector, SelectorLib} from "../../contracts/type/Selector.sol"; +import {ServiceAuthorizationV3} from "../../contracts/registry/ServiceAuthorizationV3.sol"; +import {Staking} from "../../contracts/staking/Staking.sol"; +import {StakingManager} from "../../contracts/staking/StakingManager.sol"; +import {StakingReader} from "../../contracts/staking/StakingReader.sol"; +import {StakingStore} from "../../contracts/staking/StakingStore.sol"; +import {TokenRegistry} from "../../contracts/registry/TokenRegistry.sol"; +import {VersionPart, VersionPartLib} from "../../contracts/type/Version.sol"; // solhint-disable-next-line max-states-count contract GifDeployerTest is GifDeployer { - IERC20Metadata public dip; - ChainNft public chainNft; - Registry public registry; - TokenRegistry public tokenRegistry; - ReleaseRegistry public releaseRegistry; - RegistryAdmin public registryAdmin; - StakingManager public stakingManager; - Staking public staking; + VersionPart public gifV3; + IServiceAuthorization public serviceAuthorization; - VersionPart public gifV3 = VersionPartLib.toVersionPart(3); - IServiceAuthorization public serviceAuthorization = new ServiceAuthorizationV3("85b428cbb5185aee615d101c2554b0a58fb64810"); - address public globalRegistry = makeAddr("globalRegistry"); - address public registryOwner = makeAddr("registryOwner"); - address public gifAdmin = registryOwner; - address public gifManager = registryOwner; - address public stakingOwner = registryOwner; + function setUp() public virtual { + gifV3 = VersionPartLib.toVersionPart(3); + serviceAuthorization = new ServiceAuthorizationV3(COMMIT_HASH); + + _deployCore(gifAdmin, gifManager); + } + + + function test_gifDeployerSetup() public { + _printCoreSetup(); + _printAuthz(registryAdmin, "registryAdmin"); + } + + + function test_gifDeployerRegistrySetup() public { + // GIVEN + _deployRelease(); + + // THEN + _printAuthz(registryAdmin, "RegistryAdmin"); + } - function test_deployerCoreDip() public view { + function test_gifDeployerReleaseSetup() public { + // GIVEN + _deployRelease(); + + // THEN + ReleaseAdmin releaseAdmin = ReleaseAdmin( + releaseRegistry.getReleaseInfo( + releaseRegistry.getLatestVersion()).releaseAdmin); + + _printAuthz(releaseAdmin, "ReleaseAdmin"); + } + + + function test_gifDeployerCoreDip() public view { assertTrue(address(dip) != address(0), "dip address zero"); assertEq(dip.decimals(), 18, "unexpected decimals for dip"); } - function test_deployerCoreRegistry() public view { + function test_gifDeployerCoreRegistry() public view { assertTrue(address(registry) != address(0), "registry address zero"); // check registry @@ -81,7 +103,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreTokenRegistry() public view { + function test_gifDeployerCoreTokenRegistry() public view { assertTrue(address(tokenRegistry) != address(0), "token registry address zero"); assertEq(address(tokenRegistry.getDipToken()), address(dip), "unexpected dip address"); @@ -92,7 +114,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreReleaseRegistry() public view { + function test_gifDeployerCoreReleaseRegistry() public view { assertTrue(address(releaseRegistry) != address(0), "release manager address zero"); // check authority @@ -107,7 +129,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreRegistryAccessManager() public { + function test_gifDeployerCoreRegistryAccessManager() public { assertTrue(address(registryAdmin) != address(0), "registry admin manager address zero"); assertTrue(registryAdmin.authority() != address(0), "registry admin manager authority address zero"); @@ -136,7 +158,7 @@ contract GifDeployerTest is GifDeployer { // TODO amend once full gif setup is streamlined } - function test_deployerCoreStakingManager() public view { + function test_gifDeployerCoreStakingManager() public view { assertTrue(address(stakingManager) != address(0), "staking manager address zero"); // assertEq(stakingOwner, registryOwner, "unexpected staking owner"); @@ -146,7 +168,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreStakingContract() public view { + function test_gifDeployerCoreStakingContract() public view { assertTrue(address(staking) != address(0), "staking address zero"); // check nft id @@ -176,7 +198,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreStakingReader() public { + function test_gifDeployerCoreStakingReader() public { StakingReader reader = StakingReader(staking.getStakingReader()); assertEq(address(reader.getRegistry()), address(registry), "unexpected registry address"); @@ -184,7 +206,7 @@ contract GifDeployerTest is GifDeployer { } - function test_deployerCoreStakingStore() public view { + function test_gifDeployerCoreStakingStore() public view { StakingStore store = StakingStore(staking.getStakingStore()); // check authority @@ -192,23 +214,7 @@ contract GifDeployerTest is GifDeployer { } - function setUp() public virtual { - ( - dip, - registry, - tokenRegistry, - releaseRegistry, - registryAdmin, - stakingManager, - staking - ) = deployCore( - globalRegistry, - gifAdmin, - gifManager, - stakingOwner); - - chainNft = ChainNft(registry.getChainNftAddress()); - + function _deployRelease() internal { deployRelease( releaseRegistry, serviceAuthorization, diff --git a/test/base/GifTest.sol b/test/base/GifTest.sol index e8dd52a49..826c7b668 100644 --- a/test/base/GifTest.sol +++ b/test/base/GifTest.sol @@ -18,7 +18,6 @@ import {ACTIVE} from "../../contracts/type/StateId.sol"; import {Timestamp} from "../../contracts/type/Timestamp.sol"; import {VersionPartLib} from "../../contracts/type/Version.sol"; - import {BasicOracleAuthorization} from "../../contracts/oracle/BasicOracleAuthorization.sol"; import {BasicProductAuthorization} from "../../contracts/product/BasicProductAuthorization.sol"; import {SimpleDistributionAuthorization} from "../../contracts/examples/unpermissioned/SimpleDistributionAuthorization.sol"; @@ -68,20 +67,8 @@ contract GifTest is GifDeployer { // bundle lifetime is one year in seconds uint256 constant public DEFAULT_BUNDLE_LIFETIME = 365 * 24 * 3600; - IERC20Metadata public dip; IERC20Metadata public token; - RegistryAdmin public registryAdmin; - Registry public registry; - ChainNft public chainNft; - ReleaseRegistry public releaseRegistry; - TokenRegistry public tokenRegistry; - - StakingManager public stakingManager; - Staking public staking; - StakingReader public stakingReader; - NftId public stakingNftId; - AccessManagerCloneable public masterAccessManager; InstanceAdmin public masterInstanceAdmin; InstanceAuthorizationV3 public instanceAuthorizationV3; @@ -109,16 +96,11 @@ contract GifTest is GifDeployer { SimpleProduct public product; NftId public productNftId; - address public registryAddress; - NftId public registryNftId; NftId public bundleNftId; uint256 public initialCapitalAmount; address constant public NFT_LOCK_ADDRESS = address(0x1); - address public globalRegistry = makeAddr("globalRegistry"); - address public registryOwner = makeAddr("registryOwner"); - address public stakingOwner = registryOwner; address public instanceOwner = makeAddr("instanceOwner"); address public productOwner = makeAddr("productOwner"); address public oracleOwner = makeAddr("oracleOwner"); @@ -155,42 +137,46 @@ contract GifTest is GifDeployer { function setUp() public virtual { _setUp(); - // poolCollateralizationLevelIs100, - // DEFAULT_BUNDLE_CAPITALIZATION, - // DEFAULT_BUNDLE_LIFETIME); } - function _setUp( - // UFixed poolCollateralizationLevel, - // uint256 initialBundleCapitalization, - // uint256 bundleLifetime - ) + function _setUp() internal virtual { - // solhint-disable-next-line - console.log("tx origin", tx.origin); - address gifAdmin = registryOwner; address gifManager = registryOwner; - // deploy registry, services, master instance and token + // solhint-disable + console.log("=== GifTest starting ======================================="); + console.log("=== deploying core ========================================="); + // solhint-enable _deployCore(gifAdmin, gifManager); + + // solhint-disable-next-line + console.log("=== deploying release ======================================"); _deployAndRegisterServices(gifAdmin, gifManager); + // solhint-disable-next-line + console.log("=== deploying master instance =============================="); vm.startPrank(registryOwner); _deployMasterInstance(); vm.stopPrank(); + // solhint-disable-next-line + console.log("=== register token ========================================="); vm.startPrank(address(registryOwner)); _deployRegisterAndActivateToken(); vm.stopPrank(); - // create an instance (cloned from master instance) + // solhint-disable-next-line + console.log("=== create instance ========================================"); vm.startPrank(instanceOwner); _createInstance(); vm.stopPrank(); + // solhint-disable-next-line + console.log("=== GifTest setup complete ================================="); + // print full authz setup _printAuthz(registryAdmin, "registry"); _printAuthz(instance.getInstanceAdmin(), "instance"); @@ -237,51 +223,52 @@ contract GifTest is GifDeployer { console.log(message, gasDelta); } - function _deployCore( - address gifAdmin, - address gifManager - ) - internal - { - ( - dip, - registry, - tokenRegistry, - releaseRegistry, - registryAdmin, - stakingManager, - staking - ) = deployCore( - globalRegistry, - gifAdmin, - gifManager, - stakingOwner); - - // obtain some references - registryAddress = address(registry); - chainNft = ChainNft(registry.getChainNftAddress()); - registryNftId = registry.getNftIdForAddress(registryAddress); - stakingNftId = registry.getNftIdForAddress(address(staking)); - stakingReader = staking.getStakingReader(); - - // solhint-disable - console.log("registry deployed at", address(registry)); - console.log("registry owner", registryOwner); - - console.log("token registry deployed at", address(tokenRegistry)); - console.log("release manager deployed at", address(releaseRegistry)); - - console.log("registry access manager deployed:", address(registryAdmin)); - console.log("registry access manager authority", registryAdmin.authority()); - - console.log("staking manager deployed at", address(stakingManager)); - - console.log("staking nft id", registry.getNftIdForAddress(address(staking)).toInt()); - console.log("staking deployed at", address(staking)); - console.log("staking owner (opt 1)", registry.ownerOf(address(staking))); - console.log("staking owner (opt 2)", staking.getOwner()); - // solhint-enable - } + // TODO cleanup + // function _deployCore( + // address gifAdmin, + // address gifManager + // ) + // internal + // { + // ( + // dip, + // registry, + // tokenRegistry, + // releaseRegistry, + // registryAdmin, + // stakingManager, + // staking + // ) = deployCore( + // globalRegistry, + // gifAdmin, + // gifManager, + // stakingOwner); + + // // obtain some references + // registryAddress = address(registry); + // chainNft = ChainNft(registry.getChainNftAddress()); + // registryNftId = registry.getNftIdForAddress(registryAddress); + // stakingNftId = registry.getNftIdForAddress(address(staking)); + // stakingReader = staking.getStakingReader(); + + // // solhint-disable + // console.log("registry deployed at", address(registry)); + // console.log("registry owner", registryOwner); + + // console.log("token registry deployed at", address(tokenRegistry)); + // console.log("release manager deployed at", address(releaseRegistry)); + + // console.log("registry access manager deployed:", address(registryAdmin)); + // console.log("registry access manager authority", registryAdmin.authority()); + + // console.log("staking manager deployed at", address(stakingManager)); + + // console.log("staking nft id", registry.getNftIdForAddress(address(staking)).toInt()); + // console.log("staking deployed at", address(staking)); + // console.log("staking owner (opt 1)", registry.ownerOf(address(staking))); + // console.log("staking owner (opt 2)", staking.getOwner()); + // // solhint-enable + // } function _deployAndRegisterServices( @@ -301,13 +288,11 @@ contract GifTest is GifDeployer { assertEq(releaseRegistry.getState(releaseRegistry.getLatestVersion()).toInt(), ACTIVE().toInt(), "unexpected state for releaseRegistry after activateNextRelease"); } - function _deployMasterInstance() internal { // create instance supporting contracts masterAccessManager = new AccessManagerCloneable(); masterInstanceAdmin = new InstanceAdmin(address(masterAccessManager)); - masterInstanceStore = new InstanceStore(); masterBundleSet = new BundleSet(); masterRiskSet = new RiskSet(); @@ -329,13 +314,16 @@ contract GifTest is GifDeployer { // instance service is now ready to create cloned instances masterInstanceNftId = instanceService.setAndRegisterMasterInstance(address(masterInstance)); +console.log("g"); // setup roles, targets and function grantings instanceAuthorizationV3 = new InstanceAuthorizationV3(); +console.log("h"); masterInstanceAdmin.completeSetup( address(registry), address(masterInstance), address(instanceAuthorizationV3), VersionPartLib.toVersionPart(3)); +console.log("i"); require(address(masterInstanceAdmin.getRegistry()) == address(registry), "unexpected master instance registry"); require(masterInstanceAdmin.getRelease().toInt() == 3, "unexpected master instance release"); @@ -343,6 +331,7 @@ contract GifTest is GifDeployer { // MUST be set after instance is set up and registered // lock master instance nft chainNft.transferFrom(registryOwner, NFT_LOCK_ADDRESS, masterInstanceNftId.toInt()); +console.log("j"); // solhint-disable console.log("master instance deployed at", address(masterInstance)); @@ -562,7 +551,6 @@ contract GifTest is GifDeployer { poolOwner ); vm.stopPrank(); - poolNftId = _registerComponent(product, address(pool), "pool"); } @@ -589,7 +577,6 @@ contract GifTest is GifDeployer { new SimpleDistributionAuthorization("SimpleDistribution"), distributionOwner); vm.stopPrank(); - distributionNftId = _registerComponent(product, address(distribution), "distribution"); } @@ -604,10 +591,9 @@ contract GifTest is GifDeployer { oracle = new SimpleOracle( address(registry), productNftId, - new BasicOracleAuthorization("SimpleOracle"), + new BasicOracleAuthorization("SimpleOracle", COMMIT_HASH), oracleOwner); vm.stopPrank(); - oracleNftId = _registerComponent(product, address(oracle), "oracle"); } @@ -630,94 +616,6 @@ contract GifTest is GifDeployer { // solhint-enable } - - function _printAuthz( - IAccessAdmin aa, - string memory aaName - ) - internal - { - // solhint-disable no-console - console.log("=========================================="); - console.log(aaName, registry.getObjectAddress(aa.getLinkedNftId())); - console.log(aaName, "nft id", aa.getLinkedNftId().toInt()); - console.log(aaName, "owner", aa.getLinkedOwner()); - console.log(aaName, "admin authorization"); - console.log(aaName, "admin contract:", address(aa)); - console.log(aaName, "admin authority:", aa.authority()); - - uint256 roles = aa.roles(); - uint256 targets = aa.targets(); - - console.log("------------------------------------------"); - console.log("roles", aa.roles()); - // solhint-enable - - for(uint256 i = 0; i < aa.roles(); i++) { - _printRoleMembers(aa, aa.getRoleId(i)); - } - - // solhint-disable no-console - console.log("------------------------------------------"); - console.log("targets", aa.targets()); - // solhint-enable - - for(uint256 i = 0; i < aa.targets(); i++) { - _printTarget(aa, aa.getTargetAddress(i)); - } - } - - - function _printRoleMembers(IAccessAdmin aa, RoleId roleId) internal { - IAccessAdmin.RoleInfo memory info = aa.getRoleInfo(roleId); - uint256 members = aa.roleMembers(roleId); - - // solhint-disable no-console - console.log("role", info.name.toString(), "id", roleId.toInt()); - - if (members > 0) { - for(uint i = 0; i < members; i++) { - address memberAddress = aa.getRoleMember(roleId, i); - string memory targetName = "(not target)"; - if (aa.targetExists(memberAddress)) { - targetName = aa.getTargetInfo(memberAddress).name.toString(); - } - - console.log("-", i, aa.getRoleMember(roleId, i), targetName); - } - } else { - console.log("- no role members"); - } - - console.log(""); - // solhint-enable - } - - function _printTarget(IAccessAdmin aa, address target) internal view { - IAccessAdmin.TargetInfo memory info = aa.getTargetInfo(target); - - // solhint-disable no-console - uint256 functions = aa.authorizedFunctions(target); - console.log("target", info.name.toString(), "address", target); - - if (functions > 0) { - for(uint256 i = 0; i < functions; i++) { - ( - IAccess.FunctionInfo memory func, - RoleId roleId - ) = aa.getAuthorizedFunction(target, i); - string memory role = aa.getRoleInfo(roleId).name.toString(); - - console.log("-", i, string(abi.encodePacked(func.name.toString(), "(): role ", role,":")), roleId.toInt()); - } - } else { - console.log("- no authorized functions"); - } - - console.log(""); - // solhint-enable - } - function assertEq(Amount amount1, Amount amount2, string memory message) internal { assertEq(amount1.toInt(), amount2.toInt(), message); } diff --git a/test/component/registration/ComponentTracking.t.sol b/test/component/registration/ComponentTracking.t.sol index 680582db9..5ce037641 100644 --- a/test/component/registration/ComponentTracking.t.sol +++ b/test/component/registration/ComponentTracking.t.sol @@ -178,7 +178,7 @@ contract ComponentTrackingTest is GifTest { return new SimpleOracle( address(registry), productNftId, - new BasicOracleAuthorization(name), + new BasicOracleAuthorization(name, COMMIT_HASH), owner); } diff --git a/test/examples/composeability/PoolWithReinsuranceAuthorization.sol b/test/examples/composeability/PoolWithReinsuranceAuthorization.sol index b17e94a6f..0c46032e7 100644 --- a/test/examples/composeability/PoolWithReinsuranceAuthorization.sol +++ b/test/examples/composeability/PoolWithReinsuranceAuthorization.sol @@ -23,6 +23,14 @@ contract PoolWithReinsuranceAuthorization BasicPoolAuthorization("PoolWithReinsurance") {} + function _setupServiceTargets() + internal + virtual override + { + super._setupServiceTargets(); + _authorizeServiceDomain(CLAIM(), address(14)); + } + function _setupTargetAuthorizations() internal virtual override diff --git a/test/examples/composeability/ProductWithReinsuranceAuthorization.sol b/test/examples/composeability/ProductWithReinsuranceAuthorization.sol index 47866ec72..31be38699 100644 --- a/test/examples/composeability/ProductWithReinsuranceAuthorization.sol +++ b/test/examples/composeability/ProductWithReinsuranceAuthorization.sol @@ -22,6 +22,14 @@ contract ProductWithReinsuranceAuthorization BasicProductAuthorization("ProductWithReinsurance") {} + function _setupServiceTargets() + internal + virtual override + { + super._setupServiceTargets(); + _authorizeServiceDomain(POOL(), address(15)); + } + function _setupTargetAuthorizations() internal virtual override diff --git a/test/instance/InstanceAuthorization.t.sol b/test/instance/authorization/InstanceAuthorization.t.sol similarity index 57% rename from test/instance/InstanceAuthorization.t.sol rename to test/instance/authorization/InstanceAuthorization.t.sol index a55878eee..86e715c1d 100644 --- a/test/instance/InstanceAuthorization.t.sol +++ b/test/instance/authorization/InstanceAuthorization.t.sol @@ -1,18 +1,19 @@ // SPDX-License-Identifier: APACHE-2.0 pragma solidity ^0.8.20; -import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; -import {Test, console} from "../../lib/forge-std/src/Test.sol"; - -import {InstanceAuthorizationV3} from "../../contracts/instance/InstanceAuthorizationV3.sol"; - -import {Amount, AmountLib} from "../../contracts/type/Amount.sol"; -import {Fee, FeeLib} from "../../contracts/type/Fee.sol"; -import {GifTest} from "../base/GifTest.sol"; -import {InstanceAdmin} from "../../contracts/instance/InstanceAdmin.sol"; -import {InstanceReader} from "../../contracts/instance/InstanceReader.sol"; -import {NftId} from "../../contracts/type/NftId.sol"; -import {UFixed, UFixedLib} from "../../contracts/type/UFixed.sol"; +// TODO cleanup imports +// import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; +import {Test, console} from "../../../lib/forge-std/src/Test.sol"; + +import {InstanceAuthorizationV3} from "../../../contracts/instance/InstanceAuthorizationV3.sol"; + +// import {Amount, AmountLib} from "../../../contracts/type/Amount.sol"; +// import {Fee, FeeLib} from "../../../contracts/type/Fee.sol"; +// import {GifTest} from "../../base/GifTest.sol"; +// import {InstanceAdmin} from "../../../contracts/instance/InstanceAdmin.sol"; +// import {InstanceReader} from "../../../contracts/instance/InstanceReader.sol"; +// import {NftId} from "../../../contracts/type/NftId.sol"; +// import {UFixed, UFixedLib} from "../../../contracts/type/UFixed.sol"; contract InstanceAuthorizationV3Instance is Test { diff --git a/test/instance/authorization/InstanceAuthzRoles.t.sol b/test/instance/authorization/InstanceAuthzRoles.t.sol index 642c2b05f..405fe73d1 100644 --- a/test/instance/authorization/InstanceAuthzRoles.t.sol +++ b/test/instance/authorization/InstanceAuthzRoles.t.sol @@ -25,8 +25,8 @@ contract InstanceAuthzRolesTest is InstanceAuthzBaseTest { _printRoles(); // check initial roles - assertEq(instanceAdmin.roles(), 15, "unexpected initial instance roles count (admin)"); - assertEq(instanceReader.roles(), 15, "unexpected initial instance roles count (reader)"); + assertEq(instanceAdmin.roles(), 19, "unexpected initial instance roles count (admin)"); + assertEq(instanceReader.roles(), 19, "unexpected initial instance roles count (reader)"); } //--- role creation ----------------------------------------------------// diff --git a/test/instance/authorization/InstanceAuthzTargets.t.sol b/test/instance/authorization/InstanceAuthzTargets.t.sol index f0a7880ef..03ee0c141 100644 --- a/test/instance/authorization/InstanceAuthzTargets.t.sol +++ b/test/instance/authorization/InstanceAuthzTargets.t.sol @@ -3,13 +3,13 @@ pragma solidity ^0.8.20; import {console} from "../../../lib/forge-std/src/Test.sol"; -import {AccessManagedMock} from "../../mock/AccessManagedMock.sol"; import {IAccess} from "../../../contracts/authorization/IAccess.sol"; import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; import {IInstance} from "../../../contracts/instance/IInstance.sol"; -import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; +import {AccessManagedMock} from "../../mock/AccessManagedMock.sol"; import {InstanceAuthzBaseTest} from "./InstanceAuthzBase.t.sol"; +import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; import {RoleId, RoleIdLib, ADMIN_ROLE, INSTANCE_OWNER_ROLE} from "../../../contracts/type/RoleId.sol"; import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; @@ -21,8 +21,8 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { _printTargets(); // check initial roles - assertEq(instanceAdmin.targets(), 16, "unexpected initial instance target count (admin)"); - assertEq(instanceReader.targets(), 16, "unexpected initial instance target count (reader)"); + assertEq(instanceAdmin.targets(), 5, "unexpected initial instance target count (admin)"); + assertEq(instanceReader.targets(), 5, "unexpected initial instance target count (reader)"); } @@ -47,7 +47,7 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { IAccess.TargetInfo memory targetInfo = instanceReader.getTargetInfo(address(target)); assertEq(targetInfo.name.toString(), "MyTarget", "unexpected target name"); - assertTrue(targetInfo.isCustom, "target type not custom"); + assertTrue(targetInfo.targetType == IAccess.TargetType.Custom, "target type not custom"); assertEq(targetInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected target creation time"); } diff --git a/test/mock/ServiceAuthorizationMock.sol b/test/mock/ServiceAuthorizationMock.sol index 09cd34e14..5cfcc59d7 100644 --- a/test/mock/ServiceAuthorizationMock.sol +++ b/test/mock/ServiceAuthorizationMock.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { - ObjectType, ALL, REGISTRY, SERVICE, PRODUCT, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, DISTRIBUTOR, APPLICATION, POLICY, CLAIM, BUNDLE, STAKE, STAKING, PRICE + ObjectType, ALL, RELEASE, REGISTRY, SERVICE, PRODUCT, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, DISTRIBUTOR, APPLICATION, POLICY, CLAIM, BUNDLE, STAKE, STAKING, PRICE } from "../../contracts/type/ObjectType.sol"; import {VersionPart} from "../../contracts/type/Version.sol"; @@ -18,15 +18,25 @@ import {ServiceAuthorization} from "../../contracts/authorization/ServiceAuthori contract ServiceAuthorizationMockWithRegistryService is ServiceAuthorization { - constructor(VersionPart version) - ServiceAuthorization("1db548e7d69f8974042d01be522cbd5d097a0dd2", version.toInt()) + constructor(VersionPart release) + ServiceAuthorization( + "ReleaseAdmin", + RELEASE(), + uint8(release.toInt()), + COMMIT_HASH) {} function _setupDomains() internal override { - _authorizeDomain(REGISTRY(), address(1)); + _authorizeServiceDomain(REGISTRY(), address(1)); + _authorizeServiceDomain(APPLICATION(), address(2)); + _authorizeServiceDomain(BUNDLE(), address(3)); + _authorizeServiceDomain(DISTRIBUTION(), address(4)); + _authorizeServiceDomain(COMPONENT(), address(5)); + _authorizeServiceDomain(INSTANCE(), address(6)); + _authorizeServiceDomain(STAKING(), address(7)); } function _setupDomainAuthorizations() @@ -72,48 +82,20 @@ contract ServiceAuthorizationMockWithRegistryService } } -contract ServiceAuthorizationMock is IServiceAuthorization +contract ServiceAuthorizationMock is ServiceAuthorization { - VersionPart immutable _version; - ObjectType[] internal _serviceDomains; - constructor(VersionPart version, ObjectType[] memory domains) { - _version = version; + constructor( + VersionPart release, + ObjectType[] memory domains + ) + ServiceAuthorization( + "MockServiceAuthorization", + SERVICE(), + uint8(release.toInt()), + COMMIT_HASH + ) + { _serviceDomains = domains; } - - function getRelease() external view returns(VersionPart) { - return _version; - } - - function getServiceDomains() external view returns(ObjectType[] memory serviceDomains) { - return _serviceDomains; - } - - - function supportsInterface(bytes4 interfaceId) external pure returns (bool) { - return interfaceId == type(IServiceAuthorization).interfaceId || interfaceId == type(IERC165).interfaceId; - } - - function getCommitHash() external view returns(string memory commitHash) { - revert("Not supported"); - } - - function getServiceDomain(uint idx) external pure returns(ObjectType serviceDomain) { - revert("Not supported"); - } - - function getServiceAddress(ObjectType serviceDomain) external pure returns(address service) { - revert("Not supported"); - } - - function getAuthorizedDomains(ObjectType serviceDomain) external pure returns(ObjectType[] memory authorizatedDomains) { - revert("Not supported"); - } - - function getAuthorizedFunctions(ObjectType serviceDomain, ObjectType authorizedDomain) external pure returns(IAccess.FunctionInfo[] memory authorizatedFunctions) { - revert("Not supported"); - } - - } \ No newline at end of file diff --git a/test/mock/ServiceMock.sol b/test/mock/ServiceMock.sol index 5ab332667..13ff17212 100644 --- a/test/mock/ServiceMock.sol +++ b/test/mock/ServiceMock.sol @@ -39,7 +39,7 @@ contract ServiceMock is RegisterableMockWithAuthority, IService { } function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(getDomain(), VersionPartLib.toVersionPart(3)); + return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(3)); } // from IVersionable @@ -73,7 +73,7 @@ contract SelfOwnedServiceMock is ServiceMock { } function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(getDomain(), VersionPartLib.toVersionPart(3)); + return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(3)); } } @@ -105,7 +105,7 @@ contract ServiceMockWithRandomInvalidType is ServiceMock { } function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(getDomain(), VersionPartLib.toVersionPart(3)); + return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(3)); } } @@ -137,7 +137,7 @@ contract ServiceMockWithRandomInvalidAddress is ServiceMock { } function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(getDomain(), VersionPartLib.toVersionPart(3)); + return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(3)); } } @@ -157,7 +157,7 @@ contract ServiceMockOldVersion is ServiceMock { } function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(getDomain(), VersionPartLib.toVersionPart(2)); + return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(2)); } function getRelease() public pure override(IRelease, RegisterableMockWithAuthority) returns(VersionPart) { @@ -185,7 +185,7 @@ contract ServiceMockNewVersion is ServiceMock { } function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(getDomain(), VersionPartLib.toVersionPart(4)); + return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(4)); } function getVersion() public pure override returns(Version) { @@ -211,7 +211,7 @@ contract ServiceMockWithRegistryDomainV4 is ServiceMockWithRegistryDomainV3 {} function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(getDomain(), VersionPartLib.toVersionPart(4)); + return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(4)); } function getVersion() public pure override returns(Version) { @@ -226,7 +226,7 @@ contract ServiceMockWithRegistryDomainV5 is ServiceMockWithRegistryDomainV3 {} function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.roleForTypeAndVersion(getDomain(), VersionPartLib.toVersionPart(5)); + return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(5)); } function getVersion() public pure override returns(Version) { diff --git a/test/registry/RegistryTestBase.sol b/test/registry/RegistryTestBase.sol index b31ba4ea5..5777b5f1b 100644 --- a/test/registry/RegistryTestBase.sol +++ b/test/registry/RegistryTestBase.sol @@ -55,33 +55,33 @@ contract RegistryTestBase is GifDeployer, FoundryRandom { RegistryServiceManagerMock public registryServiceManagerMock; RegistryServiceMock public registryServiceMock; - IERC20Metadata public dip; - - address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet - address public registryOwner = makeAddr("registryOwner"); + // TODO cleanup + // IERC20Metadata public dip; + // address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet + // address public registryOwner = makeAddr("registryOwner"); address public outsider = makeAddr("outsider"); - address public gifAdmin = registryOwner; - address public gifManager = registryOwner; - address public stakingOwner = registryOwner; + // address public gifAdmin = registryOwner; + // address public gifManager = registryOwner; + // address public stakingOwner = registryOwner; AccessManager public accessManager; - RegistryAdmin public registryAdmin; - Registry public registry; - ChainNft public chainNft; - ReleaseRegistry public releaseRegistry; - TokenRegistry public tokenRegistry; - StakingManager public stakingManager; - Staking public staking; + // RegistryAdmin public registryAdmin; + // Registry public registry; + // ChainNft public chainNft; + // ReleaseRegistry public releaseRegistry; + // TokenRegistry public tokenRegistry; + // StakingManager public stakingManager; + // Staking public staking; StakingStore public stakingStore; - StakingReader public stakingReader; + // StakingReader public stakingReader; address public _sender; // use with _startPrank(), _stopPrank() uint public _nextId; // use with chainNft.calculateId() NftId public protocolNftId = NftIdLib.toNftId(1101); NftId public globalRegistryNftId = NftIdLib.toNftId(2101); - NftId public registryNftId; // chainId dependent - NftId public stakingNftId; // chainId dependent + // NftId public registryNftId; // chainId dependent + // NftId public stakingNftId; // chainId dependent IRegistry.ObjectInfo public protocolInfo; IRegistry.ObjectInfo public globalRegistryInfo; // chainId dependent diff --git a/test/registryService/RegistryServiceHarnessTestBase.sol b/test/registryService/RegistryServiceHarnessTestBase.sol index 4345385c7..ba1837b33 100644 --- a/test/registryService/RegistryServiceHarnessTestBase.sol +++ b/test/registryService/RegistryServiceHarnessTestBase.sol @@ -33,12 +33,13 @@ contract RegistryServiceHarnessTestBase is GifDeployer, FoundryRandom { address public registerableOwner = makeAddr("registerableOwner"); - IRegistry public registry; - address public registryAddress; - address public globalRegistry = makeAddr("globalRegistry"); - address public registryOwner = makeAddr("registryOwner"); + // TODO cleanup + // IRegistry public registry; + // address public registryAddress; + // address public globalRegistry = makeAddr("globalRegistry"); + // address public registryOwner = makeAddr("registryOwner"); address public outsider = makeAddr("outsider"); - ReleaseRegistry releaseRegistry; + // ReleaseRegistry releaseRegistry; RegistryServiceManagerMockWithHarness public registryServiceManagerWithHarness; RegistryServiceHarness public registryServiceHarness; @@ -46,6 +47,9 @@ contract RegistryServiceHarnessTestBase is GifDeployer, FoundryRandom { function setUp() public virtual { + globalRegistry = makeAddr("globalRegistry"); + registryOwner = makeAddr("registryOwner"); + // solhint-disable-next-line console.log("tx origin", tx.origin); diff --git a/test/release/ReleaseRegistryConcrete.t.sol b/test/release/ReleaseRegistryConcrete.t.sol index b136e2462..b25c3a243 100644 --- a/test/release/ReleaseRegistryConcrete.t.sol +++ b/test/release/ReleaseRegistryConcrete.t.sol @@ -45,20 +45,26 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { // keep identical to IRegistry events event LogServiceRegistration(VersionPart majorVersion, ObjectType serviceDomain); - address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet - address public gifAdmin = makeAddr("gifAdmin"); - address public gifManager = makeAddr("gifManager"); - address public stakingOwner = makeAddr("stakingOwner"); + // TODO cleanup + // address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet + // address public gifAdmin = makeAddr("gifAdmin"); + // address public gifManager = makeAddr("gifManager"); + // address public stakingOwner = makeAddr("stakingOwner"); address public outsider = makeAddr("outsider"); - RegistryAdmin registryAdmin; - IRegistry registry; - ChainNft chainNft; - ReleaseRegistry releaseRegistry; - NftId registryNftId; + // TODO cleanup + // RegistryAdmin registryAdmin; + // IRegistry registry; + // ChainNft chainNft; + // ReleaseRegistry releaseRegistry; + // NftId registryNftId; function setUp() public virtual { + gifAdmin = makeAddr("gifAdmin"); + gifManager = makeAddr("gifManager"); + stakingOwner = makeAddr("stakingOwner"); + ( ,//dip, registry, @@ -516,7 +522,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { NftId serviceNftId = releaseRegistry.registerService(service); nextReleaseInfo.state = DEPLOYED(); - RoleId expectedServiceRoleId = RoleIdLib.roleForTypeAndVersion(REGISTRY(), nextVersion); + RoleId expectedServiceRoleId = RoleIdLib.toServiceRoleId(REGISTRY(), nextVersion); // check registration assertEq(serviceNftId.toInt(), expectedNftId, "registerService() return unexpected value"); @@ -656,7 +662,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { NftId serviceNftId = releaseRegistry.registerService(service); nextReleaseInfo.state = DEPLOYED(); - RoleId expectedServiceRoleId = RoleIdLib.roleForTypeAndVersion(REGISTRY(), nextVersion); + RoleId expectedServiceRoleId = RoleIdLib.toServiceRoleId(REGISTRY(), nextVersion); // check registration assertEq(serviceNftId.toInt(), expectedNftId, "registerService() return unexpected value"); @@ -694,7 +700,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { nextReleaseInfo.state = ACTIVE(); nextReleaseInfo.activatedAt = TimestampLib.blockTimestamp(); nextReleaseInfo.disabledAt = TimestampLib.max(); - RoleId expectedServiceRoleIdForAllVersions = RoleIdLib.roleForTypeAndAllVersions(REGISTRY()); + RoleId expectedServiceRoleIdForAllVersions = RoleIdLib.toGenericServiceRoleId(REGISTRY()); // check activation assertFalse(AccessManagerCloneable( @@ -816,7 +822,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { NftId serviceNftId = releaseRegistry.registerService(service); nextReleaseInfo.state = DEPLOYED(); - RoleId expectedServiceRoleId = RoleIdLib.roleForTypeAndVersion(REGISTRY(), nextVersion); + RoleId expectedServiceRoleId = RoleIdLib.toServiceRoleId(REGISTRY(), nextVersion); // check registration assertEq(serviceNftId.toInt(), expectedNftId, "registerService() return unexpected value"); @@ -849,7 +855,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { nextReleaseInfo.state = ACTIVE(); nextReleaseInfo.activatedAt = TimestampLib.blockTimestamp(); nextReleaseInfo.disabledAt = TimestampLib.max(); - RoleId expectedServiceRoleIdForAllVersions = RoleIdLib.roleForTypeAndAllVersions(REGISTRY()); + RoleId expectedServiceRoleIdForAllVersions = RoleIdLib.toGenericServiceRoleId(REGISTRY()); // check activation assertFalse(AccessManagerCloneable(ReleaseAdmin(nextReleaseInfo.releaseAdmin) @@ -982,7 +988,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { NftId serviceNftId = releaseRegistry.registerService(service); nextReleaseInfo.state = DEPLOYED(); - RoleId expectedServiceRoleId = RoleIdLib.roleForTypeAndVersion(REGISTRY(), nextVersion); + RoleId expectedServiceRoleId = RoleIdLib.toServiceRoleId(REGISTRY(), nextVersion); // check registration assertEq(serviceNftId.toInt(), expectedNftId, "registerService() return unexpected value"); @@ -1016,7 +1022,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { nextReleaseInfo.state = ACTIVE(); nextReleaseInfo.activatedAt = TimestampLib.blockTimestamp(); nextReleaseInfo.disabledAt = TimestampLib.max(); - RoleId expectedServiceRoleIdForAllVersions = RoleIdLib.roleForTypeAndAllVersions(REGISTRY()); + RoleId expectedServiceRoleIdForAllVersions = RoleIdLib.toGenericServiceRoleId(REGISTRY()); // check activation assertFalse(AccessManagerCloneable(ReleaseAdmin(nextReleaseInfo.releaseAdmin) diff --git a/test/release/ReleaseRegistryFuzz.t.sol b/test/release/ReleaseRegistryFuzz.t.sol index 6b886a55d..c1fe840d0 100644 --- a/test/release/ReleaseRegistryFuzz.t.sol +++ b/test/release/ReleaseRegistryFuzz.t.sol @@ -42,23 +42,28 @@ contract ReleaseRegistryTest is GifDeployer, FoundryRandom { // keep identical to IRegistry events event LogServiceRegistration(VersionPart majorVersion, ObjectType serviceDomain); - address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet - address public gifAdmin = makeAddr("gifAdmin"); - address public gifManager = makeAddr("gifManager"); - address public stakingOwner = makeAddr("stakingOwner"); + // TODO cleanup + // address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet + // address public gifAdmin = makeAddr("gifAdmin"); + // address public gifManager = makeAddr("gifManager"); + // address public stakingOwner = makeAddr("stakingOwner"); address public outsider = makeAddr("outsider"); - RegistryAdmin registryAdmin; - IRegistry registry; - ChainNft chainNft; - ReleaseRegistry releaseRegistry; - NftId registryNftId; + // RegistryAdmin registryAdmin; + // IRegistry registry; + // ChainNft chainNft; + // ReleaseRegistry releaseRegistry; + // NftId registryNftId; mapping(VersionPart version => IService) serviceByVersion; function setUp() public virtual { + gifAdmin = makeAddr("gifAdmin"); + gifManager = makeAddr("gifManager"); + stakingOwner = makeAddr("stakingOwner"); + ( ,//dip, registry, diff --git a/test/staking/Staking.t.sol b/test/staking/Staking.t.sol index 57fec701b..726621a6d 100644 --- a/test/staking/Staking.t.sol +++ b/test/staking/Staking.t.sol @@ -716,7 +716,6 @@ contract StakingTest is GifTest { // wait some time _wait(SecondsLib.oneYear()); - NftId zeroNftId = NftIdLib.zero(); // WHEN - restake to new target (NftId stakeNftId2, Amount restakedAmount) = stakingService.restakeToNewTarget(stakeNftId, instanceNftId2); @@ -832,7 +831,7 @@ contract StakingTest is GifTest { Seconds lockingPeriod2 = stakingReader.getTargetInfo(instanceNftId2).lockingPeriod; vm.stopPrank(); - (, Amount dipAmount) = _prepareAccount(staker, 3000); + _prepareAccount(staker, 3000); vm.startPrank(instanceOwner); @@ -853,10 +852,10 @@ contract StakingTest is GifTest { // create a second instance - restake target vm.startPrank(instanceOwner2); (instance2, instanceNftId2) = instanceService.createInstance(); - Seconds lockingPeriod2 = stakingReader.getTargetInfo(instanceNftId2).lockingPeriod; + stakingReader.getTargetInfo(instanceNftId2).lockingPeriod; vm.stopPrank(); - (, Amount dipAmount) = _prepareAccount(staker, 3000); + _prepareAccount(staker, 3000); vm.startPrank(staker); @@ -947,7 +946,7 @@ contract StakingTest is GifTest { // create a second instance - restake target vm.startPrank(instanceOwner2); (instance2, instanceNftId2) = instanceService.createInstance(); - Seconds lockingPeriod = stakingReader.getTargetInfo(instanceNftId).lockingPeriod; + stakingReader.getTargetInfo(instanceNftId).lockingPeriod; vm.stopPrank(); (, Amount dipAmount) = _prepareAccount(staker, 3000); From df3c77c99381b6f56528e544ea713719d478e3bd Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Fri, 30 Aug 2024 22:55:49 +0000 Subject: [PATCH 09/18] all custom targets are created with associated contract roles --- contracts/authorization/AccessAdmin.sol | 13 +- .../authorization/ServiceAuthorization.sol | 26 +- .../distribution/DistributionService.sol | 60 +-- contracts/instance/IInstance.sol | 2 +- contracts/instance/IInstanceService.sol | 4 +- contracts/instance/Instance.sol | 5 +- contracts/instance/InstanceAdmin.sol | 3 +- contracts/instance/InstanceService.sol | 6 +- contracts/pool/PoolLib.sol | 44 +++ contracts/pool/PoolService.sol | 22 -- scripts/libs/services.ts | 1 + .../authorization/InstanceAuthzTargets.t.sol | 36 +- test/mock/RegisterableMock.sol | 3 +- test/mock/ServiceAuthorizationMock.sol | 75 +--- test/mock/ServiceMock.sol | 75 +--- test/release/ReleaseRegistryConcrete.t.sol | 360 +++++++++--------- 16 files changed, 353 insertions(+), 382 deletions(-) diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index 0f065216b..f26f7e0aa 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -641,8 +641,9 @@ emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, auth TargetType targetType ) internal + returns (RoleId contractRoleId) { - _createTarget(target, targetName, targetType, true); + return _createTarget(target, targetName, targetType, true); } @@ -664,12 +665,13 @@ emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, auth bool checkAuthority ) private + returns (RoleId contractRoleId) { // checks AccessAdminLib.checkTargetCreation(this, target, targetName, checkAuthority); // effects - _createTargetUnchecked( + contractRoleId = _createTargetUnchecked( target, targetName, targetType, @@ -722,12 +724,11 @@ emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, auth bool managed ) internal + returns (RoleId targetRoleId) { // create target role (if not existing) - ( - RoleId targetRoleId, - string memory roleName - ) = _getOrCreateTargetRoleIdAndName(target, targetName, targetType); + string memory roleName; + (targetRoleId, roleName) = _getOrCreateTargetRoleIdAndName(target, targetName, targetType); if (!roleExists(targetRoleId)) { _createRole( diff --git a/contracts/authorization/ServiceAuthorization.sol b/contracts/authorization/ServiceAuthorization.sol index c33ebc1e0..ba506d24b 100644 --- a/contracts/authorization/ServiceAuthorization.sol +++ b/contracts/authorization/ServiceAuthorization.sol @@ -86,7 +86,11 @@ contract ServiceAuthorization is _release = release; _commitHash = commitHash; - _setupAdminAndPublicRoles(); + // setup of roles defined in OpenZeppelin AccessManager + _addRole(ADMIN_ROLE(), _toRoleInfo(ADMIN_ROLE(), RoleType.Core, 1, ADMIN_ROLE_NAME())); + _addRole(PUBLIC_ROLE(), _toRoleInfo(ADMIN_ROLE(), RoleType.Core, 1, PUBLIC_ROLE_NAME())); + + // defines service domains relevant for the authorization _setupDomains(); _setupDomainAuthorizations(); } @@ -187,22 +191,10 @@ contract ServiceAuthorization is return _authorizedFunctions[target][roleId]; } - // TODO cleanup - // // ERC165 - // function supportsInterface(bytes4 interfaceId) public pure returns (bool) { - // return ( - // interfaceId == type(IServiceAuthorization).interfaceId || - // interfaceId == type(IERC165).interfaceId - // ); - // } - - - function _setupAdminAndPublicRoles() internal { - _addRole(ADMIN_ROLE(), _toRoleInfo(ADMIN_ROLE(), RoleType.Core, 1, ADMIN_ROLE_NAME())); - _addRole(PUBLIC_ROLE(), _toRoleInfo(ADMIN_ROLE(), RoleType.Core, 1, PUBLIC_ROLE_NAME())); - } - - /// @dev Overwrite this function for a specific realease. + /// @dev Defines service domains relevant for the authorization. + /// When used for ReleaseAdmin the list defines the services to be registered for the release. + /// IMPORTANT: Both the list of the service domains as well as the ordering of the domains is important. + /// Trying to register services not in this list or register services in a different order will result in an error. // solhint-disable-next-line no-empty-blocks function _setupDomains() internal virtual {} diff --git a/contracts/distribution/DistributionService.sol b/contracts/distribution/DistributionService.sol index eee814880..ce1e5f354 100644 --- a/contracts/distribution/DistributionService.sol +++ b/contracts/distribution/DistributionService.sol @@ -13,21 +13,25 @@ import {IComponents} from "../instance/module/IComponents.sol"; import {IPolicy} from "../instance/module/IPolicy.sol"; import {Amount, AmountLib} from "../type/Amount.sol"; -import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol"; +// TODO cleanup +// import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol"; import {DistributorType, DistributorTypeLib} from "../type/DistributorType.sol"; import {NftId, NftIdLib} from "../type/NftId.sol"; import {KEEP_STATE} from "../type/StateId.sol"; import {ObjectType, ACCOUNTING, COMPONENT, DISTRIBUTION, INSTANCE, DISTRIBUTION, DISTRIBUTOR, REGISTRY} from "../type/ObjectType.sol"; import {InstanceReader} from "../instance/InstanceReader.sol"; import {InstanceStore} from "../instance/InstanceStore.sol"; +// TODO PoolLib feels wrong, should likely go in a component type independent lib +import {PoolLib} from "../pool/PoolLib.sol"; import {ReferralId, ReferralStatus, ReferralLib, REFERRAL_OK, REFERRAL_ERROR_UNKNOWN, REFERRAL_ERROR_EXPIRED, REFERRAL_ERROR_EXHAUSTED} from "../type/Referral.sol"; import {Seconds} from "../type/Seconds.sol"; +import {Service} from "../shared/Service.sol"; import {Timestamp, TimestampLib} from "../type/Timestamp.sol"; import {UFixed, UFixedLib} from "../type/UFixed.sol"; contract DistributionService is - ComponentVerifyingService, + Service, IDistributionService { IAccountingService private _accountingService; @@ -73,10 +77,11 @@ contract DistributionService is external returns (DistributorType distributorType) { - (NftId distributionNftId,, IInstance instance) = _getAndVerifyActiveComponent(DISTRIBUTION()); + // _getAndVerifyActiveDistribution + (NftId distributionNftId, IInstance instance) = _getAndVerifyActiveDistribution(); { - NftId productNftId = _getProductNftId(distributionNftId); + NftId productNftId = getRegistry().getParentNftId(distributionNftId); IComponents.FeeInfo memory feeInfo = instance.getInstanceReader().getFeeInfo(productNftId); UFixed variableDistributionFees = feeInfo.distributionFee.fractionalFee; @@ -87,7 +92,7 @@ contract DistributionService is } UFixed maxDiscountPercentageLimit = variableDistributionFees - variableFeesPartsTotal; - if (maxDiscountPercentage.gt(maxDiscountPercentageLimit)) { + if (maxDiscountPercentage > maxDiscountPercentageLimit) { revert ErrorDistributionServiceMaxDiscountTooHigh(maxDiscountPercentage.toInt1000(), maxDiscountPercentageLimit.toInt1000()); } } @@ -118,7 +123,7 @@ contract DistributionService is virtual returns (NftId distributorNftId) { - (NftId distributionNftId,, IInstance instance) = _getAndVerifyActiveComponent(DISTRIBUTION()); + (NftId distributionNftId, IInstance instance) = _getAndVerifyActiveDistribution(); _checkDistributionType(instance.getInstanceReader(), distributorType, distributionNftId); distributorNftId = _registryService.registerDistributor( @@ -150,7 +155,7 @@ contract DistributionService is virtual { _checkNftType(distributorNftId, DISTRIBUTOR()); - (NftId distributionNftId,, IInstance instance) = _getAndVerifyActiveComponent(DISTRIBUTION()); + (NftId distributionNftId, IInstance instance) = _getAndVerifyActiveDistribution(); _checkDistributionType(instance.getInstanceReader(), newDistributorType, distributionNftId); IDistribution.DistributorInfo memory distributorInfo = instance.getInstanceReader().getDistributorInfo(distributorNftId); @@ -173,7 +178,7 @@ contract DistributionService is onlyNftOfType(distributorNftId, DISTRIBUTOR()) returns (ReferralId referralId) { - (NftId distributionNftId,, IInstance instance) = _getAndVerifyActiveComponent(DISTRIBUTION()); + (NftId distributionNftId, IInstance instance) = _getAndVerifyActiveDistribution(); if (bytes(code).length == 0) { revert ErrorDistributionServiceInvalidReferral(code); @@ -234,9 +239,7 @@ contract DistributionService is onlyNftOfType(distributionNftId, DISTRIBUTION()) { if (referralIsValid(distributionNftId, referralId)) { - IRegistry registry = getRegistry(); - IRegistry.ObjectInfo memory distributionInfo = registry.getObjectInfo(distributionNftId); - IInstance instance = _getInstanceForComponent(registry, distributionInfo.parentNftId); + IInstance instance = _getInstanceForDistribution(getRegistry(), distributionNftId); // update book keeping for referral info IDistribution.ReferralInfo memory referralInfo = instance.getInstanceReader().getReferralInfo(referralId); @@ -255,9 +258,7 @@ contract DistributionService is restricted() onlyNftOfType(distributionNftId, DISTRIBUTION()) { - IRegistry registry = getRegistry(); - IRegistry.ObjectInfo memory distributionInfo = registry.getObjectInfo(distributionNftId); - IInstance instance = _getInstanceForComponent(registry, distributionInfo.parentNftId); + IInstance instance = _getInstanceForDistribution(getRegistry(), distributionNftId); InstanceReader reader = instance.getInstanceReader(); InstanceStore store = instance.getInstanceStore(); @@ -294,7 +295,7 @@ contract DistributionService is onlyNftOfType(distributorNftId, DISTRIBUTOR()) returns (Amount withdrawnAmount) { - (NftId distributionNftId,, IInstance instance) = _getAndVerifyActiveComponent(DISTRIBUTION()); + (NftId distributionNftId, IInstance instance) = _getAndVerifyActiveDistribution(); InstanceReader reader = instance.getInstanceReader(); IComponents.ComponentInfo memory distributionInfo = reader.getComponentInfo(distributionNftId); @@ -304,10 +305,10 @@ contract DistributionService is // determine withdrawn amount withdrawnAmount = amount; - if (withdrawnAmount.gte(AmountLib.max())) { + if (withdrawnAmount >= AmountLib.max()) { withdrawnAmount = commissionAmount; } else { - if (withdrawnAmount.gt(commissionAmount)) { + if (withdrawnAmount > commissionAmount) { revert ErrorDistributionServiceCommissionWithdrawAmountExceedsLimit(withdrawnAmount, commissionAmount); } } @@ -339,9 +340,7 @@ contract DistributionService is return false; } - IRegistry registry = getRegistry(); - IRegistry.ObjectInfo memory distributionInfo = registry.getObjectInfo(distributionNftId); - IInstance instance = _getInstanceForComponent(registry, distributionInfo.parentNftId); + IInstance instance = _getInstanceForDistribution(getRegistry(), distributionNftId); IDistribution.ReferralInfo memory info = instance.getInstanceReader().getReferralInfo(referralId); if (info.distributorNftId.eqz()) { @@ -412,16 +411,29 @@ contract DistributionService is } - function _getInstanceForDistribution(NftId distributionNftId) + // TODO cleanup + function _getInstanceForDistribution(IRegistry registry, NftId distributionNftId) internal view returns(IInstance instance) { - NftId instanceNftId = getRegistry().getParentNftId(distributionNftId); - address instanceAddress = getRegistry().getObjectAddress(instanceNftId); - return IInstance(instanceAddress); + return PoolLib.getInstanceForComponent(registry, distributionNftId); } + + function _getAndVerifyActiveDistribution() + internal + virtual + view + returns ( + NftId poolNftId, + IInstance instance + ) + { + return PoolLib.getAndVerifyActiveComponent(getRegistry(), msg.sender, DISTRIBUTION()); + } + + function _getDomain() internal pure override returns(ObjectType) { return DISTRIBUTION(); } diff --git a/contracts/instance/IInstance.sol b/contracts/instance/IInstance.sol index 15bf7dbef..08d65515b 100644 --- a/contracts/instance/IInstance.sol +++ b/contracts/instance/IInstance.sol @@ -113,7 +113,7 @@ interface IInstance is /// @dev Creates a new custom target. /// Custom targets are intended to be used for access control helper contracts of components. /// Custom targets are not intended to be used for components. - function createTarget(address target, RoleId targetRoleId, string memory name) external; + function createTarget(address target, string memory name) external returns (RoleId contractRoleId); function setTargetFunctionRole(string memory targetName, bytes4[] calldata selectors, RoleId roleId) external; diff --git a/contracts/instance/IInstanceService.sol b/contracts/instance/IInstanceService.sol index cf16d9752..1f77e0e4b 100644 --- a/contracts/instance/IInstanceService.sol +++ b/contracts/instance/IInstanceService.sol @@ -67,8 +67,8 @@ interface IInstanceService is IService { function revokeRole(RoleId roleId, address account) external; /// @dev Creates a new custom target for the calling instance. - /// Optionally, a target role can be specified that will be assigned to the target. - function createTarget(address target, RoleId targetRoleId, string memory name) external; + /// All custom trargets are created with a corresponding contract role. + function createTarget(address target, string memory name) external returns (RoleId contractRoleId); /// @dev Locks/unlocks the specified target constrolled by the corresponding instance admin. function setTargetLocked(address target, bool locked) external; diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index 65210f6b5..0a3851778 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -238,12 +238,13 @@ contract Instance is //--- Targets ------------------------------------------------------------// /// @inheritdoc IInstance - function createTarget(address target, RoleId targetRoleId, string memory name) + function createTarget(address target, string memory name) external restricted() onlyOwner() + returns (RoleId targetRoleId) { - _instanceService.createTarget(target, targetRoleId, name); + targetRoleId = _instanceService.createTarget(target, name); emit LogInstanceCustomTargetCreated(target, targetRoleId, name); } diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index fb0975b25..73d659992 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -290,8 +290,9 @@ contract InstanceAdmin is ) external restricted() + returns (RoleId contractRoleId) { - _createManagedTarget( + return _createManagedTarget( target, name, TargetType.Custom); diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index 7977f7803..fa81650be 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -124,15 +124,15 @@ contract InstanceService is instance.getInstanceAdmin().revokeRole(roleId, account); } - // TODO refactor to not use targetRoleId /// @inheritdoc IInstanceService - function createTarget(address target, RoleId targetRoleId, string memory name) + function createTarget(address target, string memory name) external restricted() onlyInstance() + returns (RoleId contractRoleId) { IInstance instance = IInstance(msg.sender); - instance.getInstanceAdmin().createTarget(target, name); + return instance.getInstanceAdmin().createTarget(target, name); } diff --git a/contracts/pool/PoolLib.sol b/contracts/pool/PoolLib.sol index 8124ee048..24fe8cd5e 100644 --- a/contracts/pool/PoolLib.sol +++ b/contracts/pool/PoolLib.sol @@ -204,6 +204,50 @@ library PoolLib { instance = IInstance(instanceAddress); } + + function getAndVerifyActiveComponent( + IRegistry registry, + address sender, + ObjectType expectedComponentType + ) + public + view + returns ( + NftId componentNftId, + IInstance instance + ) + { + ( + IRegistry.ObjectInfo memory info, + address instanceAddress + ) = ContractLib.getAndVerifyComponent( + registry, + sender, + expectedComponentType, + true); // only active components + + componentNftId = info.nftId; + instance = IInstance(instanceAddress); + } + + + function getInstanceForComponent( + IRegistry registry, + NftId componentNftId + ) + public + view + returns ( + IInstance instance + ) + { + NftId productNftId = registry.getParentNftId(componentNftId); + NftId instanceNftId = registry.getParentNftId(productNftId); + address instanceAddress = registry.getObjectAddress(instanceNftId); + return IInstance(instanceAddress); + } + + function checkNftType( IRegistry registry, NftId nftId, diff --git a/contracts/pool/PoolService.sol b/contracts/pool/PoolService.sol index b2488a4a4..a4be062ed 100644 --- a/contracts/pool/PoolService.sol +++ b/contracts/pool/PoolService.sol @@ -726,28 +726,6 @@ contract PoolService is return PoolLib.getAndVerifyActivePool(getRegistry(), msg.sender); } - // function _getAndVerifyActivePool() - // internal - // virtual - // view - // returns ( - // NftId poolNftId, - // IInstance instance - // ) - // { - // ( - // IRegistry.ObjectInfo memory info, - // address instanceAddress - // ) = ContractLib.getAndVerifyComponent( - // getRegistry(), - // msg.sender, - // POOL(), - // true); // only active pools - - // poolNftId = info.nftId; - // instance = IInstance(instanceAddress); - // } - function _getDomain() internal pure override returns(ObjectType) { return POOL(); diff --git a/scripts/libs/services.ts b/scripts/libs/services.ts index eab9fdbe9..29cafbf02 100644 --- a/scripts/libs/services.ts +++ b/scripts/libs/services.ts @@ -328,6 +328,7 @@ export async function deployAndRegisterServices(owner: Signer, registry: Registr ContractLib: libraries.contractLibAddress, DistributorTypeLib: libraries.distributorTypeLibAddress, NftIdLib: libraries.nftIdLibAddress, + PoolLib: libraries.poolLibAddress, ReferralLib: libraries.referralLibAddress, RoleIdLib: libraries.roleIdLibAddress, SecondsLib: libraries.secondsLibAddress, diff --git a/test/instance/authorization/InstanceAuthzTargets.t.sol b/test/instance/authorization/InstanceAuthzTargets.t.sol index 03ee0c141..712b7c79b 100644 --- a/test/instance/authorization/InstanceAuthzTargets.t.sol +++ b/test/instance/authorization/InstanceAuthzTargets.t.sol @@ -26,34 +26,52 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { } - function test_instanceAuthzTargetsCreateNoRoleHappyCase() public { + function test_instanceAuthzTargetsCreateHappyCase() public { // GIVEN AccessManagedMock target = _deployAccessManagedMock(); - RoleId zeroRoleId = RoleIdLib.zero(); + RoleId expectedRoleId = RoleIdLib.toRoleId(1000000); string memory targetName = "MyTarget"; + uint256 initialTargetCount = instanceAdmin.targets(); + uint256 initialRoleCount = instanceAdmin.roles(); // WHEN + THEN vm.expectEmit(address(instance)); - emit IInstance.LogInstanceCustomTargetCreated(address(target), zeroRoleId, targetName); + emit IInstance.LogInstanceCustomTargetCreated(address(target), expectedRoleId, targetName); vm.prank(instanceOwner); - instance.createTarget(address(target), zeroRoleId, targetName); + RoleId myTargetRoleId = instance.createTarget(address(target), targetName); // THEN - assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); assertEq(instanceAdmin.targets(), initialTargetCount + 1, "unexpected target count after create (admin)"); assertEq(instanceReader.targets(), initialTargetCount + 1, "unexpected target count after create (reader)"); + assertEq(instanceReader.roles(), initialRoleCount + 1, "unexpected role count after create (reader)"); + + assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); + assertTrue(instanceReader.roleExists(myTargetRoleId), "role not existing after create"); + assertEq(myTargetRoleId.toInt(), expectedRoleId.toInt(), "unexpected target role id"); + // check target info + assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); IAccess.TargetInfo memory targetInfo = instanceReader.getTargetInfo(address(target)); assertEq(targetInfo.name.toString(), "MyTarget", "unexpected target name"); assertTrue(targetInfo.targetType == IAccess.TargetType.Custom, "target type not custom"); + assertEq(targetInfo.roleId.toInt(), expectedRoleId.toInt(), "unexpected target role id"); assertEq(targetInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected target creation time"); - } - - function test_instanceAuthzTargetsCreateWithRoleHappyCase() public { - } + // check role + assertTrue(instanceReader.roleExists(targetInfo.roleId), "role not existing after create"); + assertFalse(instanceReader.isRoleCustom(targetInfo.roleId), "role is custom"); + assertEq(instanceReader.roleMembers(targetInfo.roleId), 1, "unexpected role member count"); + assertEq(instanceReader.getRoleMember(targetInfo.roleId, 0), address(target), "target not role member"); + + IAccess.RoleInfo memory roleInfo = instanceReader.getRoleInfo(targetInfo.roleId); + assertEq(roleInfo.name.toString(), "MyTarget_Role", "unexpected role name"); + assertTrue(roleInfo.roleType == IAccess.RoleType.Contract, "unexpected role type"); + assertEq(roleInfo.maxMemberCount, 1, "unexpected max member count"); + assertEq(roleInfo.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected role creation time"); + assertEq(roleInfo.pausedAt.toInt(), TimestampLib.max().toInt(), "unexpected role pausing time"); + } function test_instanceAuthzTargetsSetTargetLockedHappyCase() public { diff --git a/test/mock/RegisterableMock.sol b/test/mock/RegisterableMock.sol index b9c623343..49d8a2853 100644 --- a/test/mock/RegisterableMock.sol +++ b/test/mock/RegisterableMock.sol @@ -7,6 +7,7 @@ import {FoundryRandom} from "foundry-random/FoundryRandom.sol"; import {InitializableERC165} from "../../contracts/shared/InitializableERC165.sol"; import {IRegisterable} from "../../contracts/shared/IRegisterable.sol"; import {IRegistry} from "../../contracts/registry/IRegistry.sol"; +import {IRelease} from "../../contracts/registry/IRelease.sol"; import {MockInterceptor} from "./MockInterceptor.sol"; import {NftId, NftIdLib} from "../../contracts/type/NftId.sol"; import {ObjectType} from "../../contracts/type/ObjectType.sol"; @@ -54,7 +55,7 @@ contract RegisterableMockWithAuthority is InitializableERC165, IRegisterable, Mo function isActive() external view returns (bool active) { return true; } // from IRegisterable - function getRelease() public virtual pure returns (VersionPart release) { + function getRelease() public virtual override pure returns (VersionPart release) { return VersionPartLib.toVersionPart(3); } diff --git a/test/mock/ServiceAuthorizationMock.sol b/test/mock/ServiceAuthorizationMock.sol index 5cfcc59d7..43123f1ac 100644 --- a/test/mock/ServiceAuthorizationMock.sol +++ b/test/mock/ServiceAuthorizationMock.sol @@ -1,18 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import { - ObjectType, ALL, RELEASE, REGISTRY, SERVICE, PRODUCT, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, DISTRIBUTOR, APPLICATION, POLICY, CLAIM, BUNDLE, STAKE, STAKING, PRICE -} from "../../contracts/type/ObjectType.sol"; -import {VersionPart} from "../../contracts/type/Version.sol"; - - -import {IAccess} from "../../contracts/authorization/IAccess.sol"; -import {IRegistryService} from "../../contracts/registry/IRegistryService.sol"; -import {IServiceAuthorization} from "../../contracts/authorization/IServiceAuthorization.sol"; +import {ObjectType, REGISTRY, RELEASE, SERVICE} from "../../contracts/type/ObjectType.sol"; import {ServiceAuthorization} from "../../contracts/authorization/ServiceAuthorization.sol"; +import {VersionPart} from "../../contracts/type/Version.sol"; contract ServiceAuthorizationMockWithRegistryService @@ -31,60 +22,14 @@ contract ServiceAuthorizationMockWithRegistryService override { _authorizeServiceDomain(REGISTRY(), address(1)); - _authorizeServiceDomain(APPLICATION(), address(2)); - _authorizeServiceDomain(BUNDLE(), address(3)); - _authorizeServiceDomain(DISTRIBUTION(), address(4)); - _authorizeServiceDomain(COMPONENT(), address(5)); - _authorizeServiceDomain(INSTANCE(), address(6)); - _authorizeServiceDomain(STAKING(), address(7)); - } - - function _setupDomainAuthorizations() - internal - override - { - _setupIRegistryServiceAuthorization(); - } - - - function _setupIRegistryServiceAuthorization() - internal - { - IAccess.FunctionInfo[] storage functions; - - functions = _authorizeForService(REGISTRY(), APPLICATION()); - _authorize(functions, IRegistryService.registerPolicy.selector, "registerPolicy"); - - // functions = _authorizeForService(REGISTRY(), POOL()); - // _authorize(functions, IRegistryService.registerPool.selector, "registerPool"); - - functions = _authorizeForService(REGISTRY(), BUNDLE()); - _authorize(functions, IRegistryService.registerBundle.selector, "registerBundle"); - - functions = _authorizeForService(REGISTRY(), DISTRIBUTION()); - // _authorize(functions, IRegistryService.registerDistribution.selector, "registerDistribution"); - _authorize(functions, IRegistryService.registerDistributor.selector, "registerDistributor"); - - functions = _authorizeForService(REGISTRY(), COMPONENT()); - _authorize(functions, IRegistryService.registerProduct.selector, "registerProduct"); - - functions = _authorizeForService(REGISTRY(), COMPONENT()); - _authorize(functions, IRegistryService.registerProductLinkedComponent.selector, "registerProductLinkedComponent"); - - functions = _authorizeForService(REGISTRY(), INSTANCE()); - _authorize(functions, IRegistryService.registerInstance.selector, "registerInstance"); - - functions = _authorizeForService(REGISTRY(), STAKING()); - _authorize(functions, IRegistryService.registerStake.selector, "registerStake"); - - // functions = _authorizeForService(REGISTRY(), PRODUCT()); - // _authorize(functions, IRegistryService.registerProduct.selector, "registerProduct"); } } contract ServiceAuthorizationMock is ServiceAuthorization { + ObjectType[] internal _domainsFromConstructor; + constructor( VersionPart release, ObjectType[] memory domains @@ -96,6 +41,16 @@ contract ServiceAuthorizationMock is ServiceAuthorization COMMIT_HASH ) { - _serviceDomains = domains; + _domainsFromConstructor = domains; + } + + function _setupDomains() + internal + override + { + for (uint256 i = 0; i < _domainsFromConstructor.length; i++) { + // address is 20 bytes which is uint160 + _authorizeServiceDomain(_domainsFromConstructor[i], address(uint160(1 + i))); + } } } \ No newline at end of file diff --git a/test/mock/ServiceMock.sol b/test/mock/ServiceMock.sol index 13ff17212..dd5bc6e30 100644 --- a/test/mock/ServiceMock.sol +++ b/test/mock/ServiceMock.sol @@ -33,13 +33,18 @@ contract ServiceMock is RegisterableMockWithAuthority, IService { _registerInterface(type(IService).interfaceId); } + // from IRegisterable / IService + function getRelease() public pure virtual override (IRelease, RegisterableMockWithAuthority) returns(VersionPart) { + return VersionPartLib.toVersionPart(3); + } + // from IService function getDomain() public pure virtual returns(ObjectType) { return PRODUCT(); } function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(3)); + return RoleIdLib.toServiceRoleId(getDomain(), getRelease()); } // from IVersionable @@ -49,12 +54,12 @@ contract ServiceMock is RegisterableMockWithAuthority, IService { virtual override// (IVersionable, Versionable) returns(Version) { - return VersionLib.toVersion(3,0,0); + return VersionLib.toVersion(uint8(getRelease().toInt()),0,0); } // from IVersionable, DON NOT USE - function initializeVersionable(address activatedBy, bytes memory activationData) external { revert(); } - function upgradeVersionable(bytes memory upgradeData) external { revert(); } + function initializeVersionable(address activatedBy, bytes memory activationData) external { revert("not implemented"); } + function upgradeVersionable(bytes memory upgradeData) external { revert("not implemented"); } } contract SelfOwnedServiceMock is ServiceMock { @@ -71,10 +76,6 @@ contract SelfOwnedServiceMock is ServiceMock { function getDomain() public pure override returns(ObjectType) { return DISTRIBUTION(); } - - function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(3)); - } } contract ServiceMockWithRandomInvalidType is ServiceMock { @@ -152,21 +153,7 @@ contract ServiceMockOldVersion is ServiceMock { initialAuthority) {} - function getDomain() public pure override returns(ObjectType) { - return PRODUCT(); // same as ServiceMock - } - - function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(2)); - } - - function getRelease() public pure override(IRelease, RegisterableMockWithAuthority) returns(VersionPart) { - return VersionPartLib.toVersionPart(2); - } - - function getVersion() public pure override returns(Version) { - return VersionLib.toVersion(2,0,0); - } + function getRelease() public pure virtual override returns(VersionPart) { return VersionPartLib.toVersionPart(2); } } contract ServiceMockNewVersion is ServiceMock { @@ -179,18 +166,6 @@ contract ServiceMockNewVersion is ServiceMock { initialOwner, initialAuthority) {} - - function getDomain() public pure override returns(ObjectType) { - return PRODUCT(); // same as ServiceMock - } - - function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(4)); - } - - function getVersion() public pure override returns(Version) { - return VersionLib.toVersion(4,0,0); - } } contract ServiceMockWithRegistryDomainV3 is ServiceMock { @@ -199,37 +174,25 @@ contract ServiceMockWithRegistryDomainV3 is ServiceMock { ServiceMock(nftId, registryNftId, isInterceptor, initialOwner, initialAuthority) {} - function getDomain() public pure virtual override returns(ObjectType) { - return REGISTRY(); - } + function getDomain() public pure virtual override returns(ObjectType) { return REGISTRY(); } } -contract ServiceMockWithRegistryDomainV4 is ServiceMockWithRegistryDomainV3 +contract ServiceMockWithRegistryDomainV4 is ServiceMock { constructor(NftId nftId, NftId registryNftId, bool isInterceptor, address initialOwner, address initialAuthority) - ServiceMockWithRegistryDomainV3(nftId, registryNftId, isInterceptor, initialOwner, initialAuthority) + ServiceMock(nftId, registryNftId, isInterceptor, initialOwner, initialAuthority) {} - function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(4)); - } - - function getVersion() public pure override returns(Version) { - return VersionLib.toVersion(4,0,0); - } + function getRelease() public pure virtual override returns(VersionPart) { return VersionPartLib.toVersionPart(4); } + function getDomain() public pure virtual override returns(ObjectType) { return REGISTRY(); } } -contract ServiceMockWithRegistryDomainV5 is ServiceMockWithRegistryDomainV3 +contract ServiceMockWithRegistryDomainV5 is ServiceMock { constructor(NftId nftId, NftId registryNftId, bool isInterceptor, address initialOwner, address initialAuthority) - ServiceMockWithRegistryDomainV3(nftId, registryNftId, isInterceptor, initialOwner, initialAuthority) + ServiceMock(nftId, registryNftId, isInterceptor, initialOwner, initialAuthority) {} - function getRoleId() external virtual override pure returns(RoleId serviceRoleId) { - return RoleIdLib.toServiceRoleId(getDomain(), VersionPartLib.toVersionPart(5)); - } - - function getVersion() public pure override returns(Version) { - return VersionLib.toVersion(5,0,0); - } + function getRelease() public pure virtual override returns(VersionPart) { return VersionPartLib.toVersionPart(5); } + function getDomain() public pure virtual override returns(ObjectType) { return REGISTRY(); } } diff --git a/test/release/ReleaseRegistryConcrete.t.sol b/test/release/ReleaseRegistryConcrete.t.sol index c96d6c0a7..eea11cc0b 100644 --- a/test/release/ReleaseRegistryConcrete.t.sol +++ b/test/release/ReleaseRegistryConcrete.t.sol @@ -1,38 +1,33 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol"; import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; import {FoundryRandom} from "foundry-random/FoundryRandom.sol"; import {console} from "../../lib/forge-std/src/Test.sol"; -import {StdUtils} from "../../lib/forge-std/src/StdUtils.sol"; - -import {VersionPart, VersionPartLib} from "../../contracts/type/Version.sol"; -import {StateIdLib, SCHEDULED, DEPLOYING, DEPLOYED, SKIPPED, ACTIVE, PAUSED} from "../../contracts/type/StateId.sol"; -import {TimestampLib, gteTimestamp} from "../../contracts/type/Timestamp.sol"; -import {NftId, NftIdLib} from "../../contracts/type/NftId.sol"; -import {RoleId, RoleIdLib} from "../../contracts/type/RoleId.sol"; -import {ObjectType, RELEASE, REGISTRY, PRODUCT} from "../../contracts/type/ObjectType.sol"; +import {IAccessAdmin} from "../../contracts/authorization/IAccessAdmin.sol"; import {ILifecycle} from "../../contracts/shared/Lifecycle.sol"; +import {IRelease} from "../../contracts/registry/IRelease.sol"; import {IService} from "../../contracts/shared/IService.sol"; - -import {IAccessAdmin} from "../../contracts/authorization/IAccessAdmin.sol"; import {IServiceAuthorization} from "../../contracts/authorization/IServiceAuthorization.sol"; -import {AccessManagerCloneable} from "../../contracts/authorization/AccessManagerCloneable.sol"; -import {RegistryAdmin} from "../../contracts/registry/RegistryAdmin.sol"; -import {ReleaseAdmin} from "../../contracts/registry/ReleaseAdmin.sol"; -import {IRegistry} from "../../contracts/registry/Registry.sol"; -import {IRelease} from "../../contracts/registry/IRelease.sol"; -import {ReleaseRegistry} from "../../contracts/registry/ReleaseRegistry.sol"; +import {AccessManagerCloneable} from "../../contracts/authorization/AccessManagerCloneable.sol"; import {ChainNft} from "../../contracts/registry/ChainNft.sol"; - +import {ContractLib} from "../../contracts/shared/ContractLib.sol"; import {GifDeployer} from "../base/GifDeployer.sol"; - -import {ServiceAuthorizationMock, ServiceAuthorizationMockWithRegistryService} from "../mock/ServiceAuthorizationMock.sol"; +import {NftId, NftIdLib} from "../../contracts/type/NftId.sol"; import {NftOwnableMock} from "../mock/NftOwnableMock.sol"; +import {ObjectType, RELEASE, REGISTRY, PRODUCT} from "../../contracts/type/ObjectType.sol"; +import {ReleaseAdmin} from "../../contracts/registry/ReleaseAdmin.sol"; +import {ReleaseRegistry} from "../../contracts/registry/ReleaseRegistry.sol"; +import {RoleId, RoleIdLib} from "../../contracts/type/RoleId.sol"; +import {ServiceAuthorizationV3} from "../../contracts/registry/ServiceAuthorizationV3.sol"; +import {ServiceAuthorizationMock, ServiceAuthorizationMockWithRegistryService} from "../mock/ServiceAuthorizationMock.sol"; import {ServiceMock, ServiceMockWithRegistryDomainV3, ServiceMockWithRegistryDomainV4, ServiceMockWithRegistryDomainV5} from "../mock/ServiceMock.sol"; +import {StateIdLib, SCHEDULED, DEPLOYING, DEPLOYED, SKIPPED, ACTIVE, PAUSED} from "../../contracts/type/StateId.sol"; +import {TimestampLib, gteTimestamp} from "../../contracts/type/Timestamp.sol"; +import {VersionPart, VersionPartLib} from "../../contracts/type/Version.sol"; + contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { @@ -45,20 +40,8 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { // keep identical to IRegistry events event LogServiceRegistration(VersionPart majorVersion, ObjectType serviceDomain); - // TODO cleanup - // address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet - // address public gifAdmin = makeAddr("gifAdmin"); - // address public gifManager = makeAddr("gifManager"); - // address public stakingOwner = makeAddr("stakingOwner"); address public outsider = makeAddr("outsider"); - // TODO cleanup - // RegistryAdmin registryAdmin; - // IRegistry registry; - // ChainNft chainNft; - // ReleaseRegistry releaseRegistry; - // NftId registryNftId; - function setUp() public virtual { gifAdmin = makeAddr("gifAdmin"); @@ -83,89 +66,6 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { registryNftId = registry.getNftId(); } - function _prepareServiceWithRegistryDomain(VersionPart releaseVersion, ReleaseAdmin releaseAdmin) - public - returns (IService service) - { - if(releaseVersion.toInt() == 3) { - service = new ServiceMockWithRegistryDomainV3( - NftIdLib.zero(), - registryNftId, - false, // isInterceptor - gifManager, - releaseAdmin.authority()); - } else if(releaseVersion.toInt() == 4) { - service = new ServiceMockWithRegistryDomainV4( - NftIdLib.zero(), - registryNftId, - false, // isInterceptor - gifManager, - releaseAdmin.authority()); - } else if(releaseVersion.toInt() == 5) { - service = new ServiceMockWithRegistryDomainV5( - NftIdLib.zero(), - registryNftId, - false, // isInterceptor - gifManager, - releaseAdmin.authority()); - } - } - - function _checkReleaseInfo(IRelease.ReleaseInfo memory info) public view - { - if(info.state == SCHEDULED()) { - assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #1"); - assertTrue(info.salt == bytes32(0), "Test error: unexpected salt #1"); - assertTrue(address(info.auth) == address(0), "Test error: unexpected auth #1"); - assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #1"); - assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #1"); - } else if (info.state == DEPLOYING() || info.state == DEPLOYED()) { - assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #2"); - assertTrue(info.salt != bytes32(0), "Test error: unexpected salt #2"); - assertTrue(address(info.auth) != address(0), "Test error: unexpected auth #2"); - assertTrue(info.auth.getRelease() == info.version, "Test error: unexpected auth version #1"); - assertTrue(info.auth.getServiceDomains().length > 0, "Test error: unexpected auth domain num #1"); - assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #2"); - assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #2"); - } else if (info.state == ACTIVE()) { - assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #3"); - assertTrue(info.salt != bytes32(0), "Test error: unexpected salt #3"); - assertTrue(address(info.auth) != address(0), "Test error: unexpected auth #3"); - assertTrue(info.auth.getRelease() == info.version, "Test error: unexpected auth version #2"); - assertTrue(info.auth.getServiceDomains().length > 0, "Test error: unexpected auth domain num #2"); - assertTrue(gteTimestamp(TimestampLib.blockTimestamp(), info.activatedAt), "Test error: unexpected activatedAt #3"); - assertTrue(info.disabledAt == TimestampLib.max(), "Test error: unexpected disabledAt #3"); - } else if (info.state == PAUSED()) { - assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #4"); - assertTrue(info.salt != bytes32(0), "Test error: unexpected salt #4"); - assertTrue(address(info.auth) != address(0), "Test error: unexpected auth #4"); - assertTrue(info.auth.getRelease() == info.version, "Test error: unexpected auth version #3"); - assertTrue(info.auth.getServiceDomains().length > 0, "Test error: unexpected auth domain num #3"); - assertTrue(gteTimestamp(TimestampLib.blockTimestamp(), info.activatedAt), "Test error: unexpected activatedAt #4"); - assertTrue(gteTimestamp(TimestampLib.blockTimestamp(), info.disabledAt), "Test error: unexpected disabledAt #4"); - assertTrue(gteTimestamp(info.disabledAt, info.activatedAt), "Test error: disabledAt < activatedAt #4"); - } else if (info.state == SKIPPED()) { - assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #5"); - // salt can have any values - if(address(info.auth) != address(0)) { - assertTrue(info.auth.getRelease() == info.version, "Test error: unexpected auth version #4"); - assertTrue(info.auth.getServiceDomains().length > 0, "Test error: unexpected auth domain num #4"); - } - assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #5"); - assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #5"); - } else if (info.state == StateIdLib.zero()) { - assertTrue(info.version.toInt() == 0, "Test error: unexpected version #6"); - assertTrue(info.salt == bytes32(0), "Test error: unexpected salt #6"); - assertTrue(address(info.auth) == address(0), "Test error: unexpected auth #6"); - assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #6"); - assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #6"); - } else { - // solhint-disable-next-line - console.log("Unexpected state ", info.state.toInt()); - assertTrue(false, "Test error: unexpected state"); - } - } - // assert by version getters function _assert_releaseRegistry_getters(VersionPart version, IRelease.ReleaseInfo memory info) public view { @@ -179,7 +79,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_setUp() public view { - for(uint i = 0; i <= releaseRegistry.INITIAL_GIF_VERSION() + 1; i++) { + for(uint256 i = 0; i <= releaseRegistry.INITIAL_GIF_VERSION() + 1; i++) { _assert_releaseRegistry_getters(VersionPartLib.toVersionPart(i), zeroReleaseInfo()); } @@ -203,7 +103,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, stakingOwner)); vm.prank(stakingOwner); - VersionPart version = releaseRegistry.createNextRelease(); + releaseRegistry.createNextRelease(); vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, outsider)); vm.prank(outsider); @@ -261,7 +161,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { IRelease.ReleaseInfo memory nextReleaseInfo; IRelease.ReleaseInfo memory prevReleaseInfo; - for(uint i = 0; i <= 10; i++) + for(uint256 i = 0; i <= 10; i++) { // create - skip vm.prank(gifAdmin); @@ -302,7 +202,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { IRelease.ReleaseInfo memory nextReleaseInfo; IRelease.ReleaseInfo memory prevReleaseInfo; - for(uint i = 0; i <= 10; i++) + for(uint256 i = 0; i <= 10; i++) { vm.prank(gifAdmin); VersionPart createdVersion = releaseRegistry.createNextRelease(); @@ -343,12 +243,6 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { bytes32 nextSalt = bytes32(randomNumber(type(uint256).max)); address nextAdmin = _getNextContractAddress(address(releaseRegistry), 0); - // // solhint-disable - // console.log("nextAdmin(0)", i, _getNextContractAddress(address(releaseRegistry), 0)); - // console.log("nextAdmin(1)", i, _getNextContractAddress(address(releaseRegistry), 1)); - // console.log("nextAdmin(2)", i, _getNextContractAddress(address(releaseRegistry), 2)); - // // solhint-enable - vm.expectEmit(address(releaseRegistry)); emit LogReleaseCreation(IAccessAdmin(nextAdmin), nextVersion, nextSalt); @@ -404,13 +298,6 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { } } - function _getNextContractAddress(address deployer, uint256 nonceOffset) internal returns (address) { - // return StdUtils.computeCreateAddress( - return vm.computeCreateAddress( - deployer, - vm.getNonce(deployer) + nonceOffset); - } - function test_releaseRegistry_createRelease_whenReleaseDeployedHappyCase() public { VersionPart nextVersion = VersionPartLib.toVersionPart(releaseRegistry.INITIAL_GIF_VERSION()); @@ -420,12 +307,15 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { IService service; - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { vm.prank(gifAdmin); VersionPart createdVersion = releaseRegistry.createNextRelease(); { + // solhint-disable-next-line + console.log("scheduling release", i); + // create - skip nextReleaseInfo.state = SCHEDULED(); nextReleaseInfo.version = nextVersion; @@ -460,9 +350,15 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { assertEq(createdVersion.toInt(), releaseRegistry.getVersion(i).toInt(), "getReleaseVersion() return unexpected value"); assertEq(releaseRegistry.getReleaseInfo(createdVersion).state.toInt(), SCHEDULED().toInt(), "getReleaseInfo() not DEPLOYING"); assertEq(releaseRegistry.isActiveRelease(createdVersion), false, "isActiveRelease() returned unexpected value"); + + // solhint-disable-next-line + console.log("scheduled release", i); } { + // solhint-disable-next-line + console.log("deploying release", i); + // prepare IServiceAuthorization nextAuthMock = new ServiceAuthorizationMockWithRegistryService(nextVersion); bytes32 nextSalt = bytes32(randomNumber(type(uint256).max)); @@ -506,9 +402,15 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { assertEq(createdVersion.toInt(), releaseRegistry.getVersion(i).toInt(), "getReleaseVersion() return unexpected value"); assertEq(releaseRegistry.getReleaseInfo(createdVersion).state.toInt(), DEPLOYING().toInt(), "getReleaseInfo() not DEPLOYING"); assertEq(releaseRegistry.isActiveRelease(createdVersion), false, "isActiveRelease() returned unexpected value"); + + // solhint-disable-next-line + console.log("deployed release", i); } { + // solhint-disable-next-line + console.log("registering services", i); + // deploy (register all(1) services) service = _prepareServiceWithRegistryDomain(nextReleaseInfo.version, ReleaseAdmin(nextReleaseInfo.releaseAdmin)); assertFalse(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #1"); @@ -546,6 +448,9 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { assertEq(createdVersion.toInt(), releaseRegistry.getVersion(i).toInt(), "getReleaseVersion() return unexpected value"); assertEq(releaseRegistry.getReleaseInfo(createdVersion).state.toInt(), DEPLOYED().toInt(), "getReleaseInfo() not DEPLOYED"); assertEq(releaseRegistry.isActiveRelease(createdVersion), false, "isActiveRelease() returned unexpected value"); + + // solhint-disable-next-line + console.log("services registered", i); } prevVersion = nextVersion; @@ -555,6 +460,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { } } + function test_releaseRegistry_createRelease_whenReleaseActiveHappyCase() public { VersionPart nextVersion = VersionPartLib.toVersionPart(releaseRegistry.INITIAL_GIF_VERSION()); @@ -564,7 +470,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { IService service; - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { vm.prank(gifAdmin); VersionPart createdVersion = releaseRegistry.createNextRelease(); @@ -646,12 +552,14 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { } { +console.log("block 3a, i:", i); // solhint-disable-next-line console.log("registering services", i); // deploy (register all(1) services) service = _prepareServiceWithRegistryDomain(nextReleaseInfo.version, ReleaseAdmin(nextReleaseInfo.releaseAdmin)); assertFalse(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #1"); +console.log("block 3b, i:", i); uint256 expectedNftId = chainNft.getNextTokenId(); @@ -661,6 +569,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { vm.prank(gifManager); NftId serviceNftId = releaseRegistry.registerService(service); +console.log("block 3c, i:", i); nextReleaseInfo.state = DEPLOYED(); RoleId expectedServiceRoleId = RoleIdLib.toServiceRoleId(REGISTRY(), nextVersion); @@ -673,13 +582,17 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { assertTrue(releaseAdmin.targetExists(address(service)), "targetExists() return unexpected value"); assertTrue(registry.isRegisteredService(address(service)), "isRegisteredService() return unexpected value #2"); assertFalse(registryAdmin.targetExists(address(service)), "targetExists() return unexpected value"); +console.log("block 3d, i:", i); _assert_releaseRegistry_getters(nextVersion, nextReleaseInfo); +console.log("block 3e, i:", i); _assert_releaseRegistry_getters(prevVersion, prevReleaseInfo); +console.log("block 3f, i:", i); assertEq(releaseRegistry.releases(), i + 1, "releases() return unexpected value #3"); assertEq(releaseRegistry.getNextVersion().toInt(), nextReleaseInfo.version.toInt(), "getNextVersion() return unexpected value #3"); assertEq(releaseRegistry.getLatestVersion().toInt(), prevReleaseInfo.version.toInt(), "getLatestVersion() return unexpected value #3"); +console.log("block 3g, i:", i); // solhint-disable-next-line console.log("registered services", i); @@ -742,7 +655,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { IService service; - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { { // create - skip @@ -913,7 +826,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { IService service; - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { { // create - skip @@ -1186,7 +1099,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_prepareRelease_whenReleaseDeploying() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -1213,7 +1126,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_prepareRelease_whenReleaseDeployed() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -1254,7 +1167,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_prepareRelease_whenReleaseActive() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -1296,7 +1209,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_prepareRelease_whenReleasePaused() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -1402,25 +1315,39 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { uint256 createdReleases = randomNumber(2, 10); uint256 releaseVersion = releaseRegistry.INITIAL_GIF_VERSION() + createdReleases; - for(uint i = 0; i <= createdReleases; i++) { + for(uint256 i = 0; i <= createdReleases; i++) { vm.prank(gifAdmin); releaseRegistry.createNextRelease(); } - for(uint i = 0; i < releaseVersion; i++) + for(uint256 i = 0; i < releaseVersion; i++) { VersionPart tooSmallVersion = VersionPartLib.toVersionPart(i); ObjectType[] memory domains = new ObjectType[](1); domains[0] = PRODUCT(); - IServiceAuthorization auth = new ServiceAuthorizationMock(tooSmallVersion, domains); + + if (i < VersionPartLib.releaseMin().toInt()) { + vm.expectRevert(abi.encodeWithSelector( + IServiceAuthorization.ErrorAuthorizationReleaseInvalid.selector, + tooSmallVersion)); + } + + IServiceAuthorization authMock = new ServiceAuthorizationMock(tooSmallVersion, domains); + // revert in constructor results in contract address 0x0000000000000000000000000000000000000001 + if (address(authMock) <= address(1)) { + console.log("i", i, "address(authMock)", address(authMock)); + continue; + } + vm.expectRevert(abi.encodeWithSelector( ReleaseRegistry.ErrorReleaseRegistryServiceAuthVersionMismatch.selector, - auth, + authMock, releaseVersion, tooSmallVersion )); + vm.prank(gifManager); - releaseRegistry.prepareNextRelease(auth, "0x1234"); + releaseRegistry.prepareNextRelease(authMock, "0x1234"); } // check prepareRelease() works for the releaseVersion @@ -1433,14 +1360,14 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { vm.startPrank(gifAdmin); - for(uint i = 0; i <= createdReleases; i++) { + for(uint256 i = 0; i <= createdReleases; i++) { releaseRegistry.createNextRelease(); } vm.stopPrank(); vm.startPrank(gifManager); - for(uint i = releaseVersion + 1; i < releaseVersion + 5; i++) + for(uint256 i = releaseVersion + 1; i < releaseVersion + 5; i++) { VersionPart tooBigVersion = VersionPartLib.toVersionPart(i); ServiceAuthorizationMockWithRegistryService auth = new ServiceAuthorizationMockWithRegistryService(tooBigVersion); @@ -1517,11 +1444,11 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_registerService_whenReleaseScheduled() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); - releaseRegistry.createNextRelease(); + VersionPart releaseVersion = releaseRegistry.createNextRelease(); ServiceMock serviceMock = new ServiceMock( NftIdLib.zero(), @@ -1572,7 +1499,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { uint initialVersionInt = releaseRegistry.INITIAL_GIF_VERSION(); VersionPart releaseVersion; - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -1586,7 +1513,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { vm.prank(gifManager); (IAccessAdmin releaseAdmin,,) = releaseRegistry.prepareNextRelease(nextAuthMock, nextSalt); - for(uint i = 0; i < 2; i++) + for(uint256 i = 0; i < 2; i++) { // register with revert VersionPart serviceVersion = VersionPartLib.toVersionPart(initialVersionInt + i); @@ -1617,7 +1544,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { vm.prank(gifManager); (IAccessAdmin releaseAdmin,,) = releaseRegistry.prepareNextRelease(nextAuthMock, nextSalt); - for(uint i = 1; i <= 2; i++) + for(uint256 i = 1; i <= 2; i++) { // register with revert VersionPart serviceVersion = VersionPartLib.toVersionPart(initialVersionInt + i); @@ -1667,7 +1594,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_registerService_whenReleaseDeployed() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -1709,7 +1636,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_registerService_whenReleaseActive() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -1755,7 +1682,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_registerService_whenReleasePaused() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -1836,7 +1763,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_activateRelease_whenReleaseScheduled() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -1856,7 +1783,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { } function test_releaseRegistry_activateRelease_whenReleaseDeploying() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -1895,7 +1822,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_activateRelease_whenReleaseActive() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -1934,7 +1861,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_activateRelease_whenReleasePaused() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -1996,7 +1923,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_pauseRelease_whenInitialReleaseNotCreated() public { // loop through first n versions - for(uint i = 0; i < 5; i++) + for(uint256 i = 0; i < 5; i++) { VersionPart version = VersionPartLib.toVersionPart(i); vm.expectRevert(abi.encodeWithSelector( @@ -2013,7 +1940,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_pauseRelease_whenReleaseScheduled() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -2034,7 +1961,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_pauseRelease_whenReleaseDeploying() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -2062,7 +1989,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_pauseRelease_whenReleaseDeployed() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -2108,7 +2035,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_pauseRelease_whenReleasePaused() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -2171,7 +2098,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_unpauseRelease_whenInitialReleaseNotCreated() public { // loop through first n versions - for(uint i = 0; i < 5; i++) + for(uint256 i = 0; i < 5; i++) { VersionPart version = VersionPartLib.toVersionPart(i); vm.expectRevert(abi.encodeWithSelector( @@ -2188,7 +2115,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_unpauseRelease_whenReleaseScheduled() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create - skip vm.prank(gifAdmin); @@ -2209,7 +2136,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_unpauseRelease_whenReleaseDeploying() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -2237,7 +2164,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_unpauseRelease_whenReleaseDeployed() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -2272,7 +2199,7 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { function test_releaseRegistry_unpauseRelease_whenReleaseActive() public { - for(uint i = 0; i <= 2; i++) + for(uint256 i = 0; i <= 2; i++) { // create vm.prank(gifAdmin); @@ -2309,15 +2236,92 @@ contract ReleaseRegistryConcreteTest is GifDeployer, FoundryRandom { } } - function test_releaseRegistry_unpauseRelease_whenReleasePausedHappyCase() public + function _getNextContractAddress(address deployer, uint256 nonceOffset) internal returns (address) { + return vm.computeCreateAddress( + deployer, + vm.getNonce(deployer) + nonceOffset); + } + + function _prepareServiceWithRegistryDomain(VersionPart releaseVersion, ReleaseAdmin releaseAdmin) + public + returns (IService service) { - // Equivalent to test_releaseRegistry_createRelease_whenReleaseUnpausedHappyCase() - // create - // prepare - // register service - // activate - // pause - // unpause - // loop + if(releaseVersion.toInt() == 3) { + service = new ServiceMockWithRegistryDomainV3( + NftIdLib.zero(), + registryNftId, + false, // isInterceptor + gifManager, + releaseAdmin.authority()); + } else if(releaseVersion.toInt() == 4) { + service = new ServiceMockWithRegistryDomainV4( + NftIdLib.zero(), + registryNftId, + false, // isInterceptor + gifManager, + releaseAdmin.authority()); + } else if(releaseVersion.toInt() == 5) { + service = new ServiceMockWithRegistryDomainV5( + NftIdLib.zero(), + registryNftId, + false, // isInterceptor + gifManager, + releaseAdmin.authority()); + } + } + + function _checkReleaseInfo(IRelease.ReleaseInfo memory info) public view + { + if(info.state == SCHEDULED()) { + assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #1"); + assertTrue(info.salt == bytes32(0), "Test error: unexpected salt #1"); + assertTrue(address(info.auth) == address(0), "Test error: unexpected auth #1"); + assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #1"); + assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #1"); + } else if (info.state == DEPLOYING() || info.state == DEPLOYED()) { + assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #2"); + assertTrue(info.salt != bytes32(0), "Test error: unexpected salt #2"); + assertTrue(address(info.auth) != address(0), "Test error: unexpected auth #2"); + assertTrue(info.auth.getRelease() == info.version, "Test error: unexpected auth version #1"); + assertTrue(info.auth.getServiceDomains().length > 0, "Test error: unexpected auth domain num #1"); + assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #2"); + assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #2"); + } else if (info.state == ACTIVE()) { + assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #3"); + assertTrue(info.salt != bytes32(0), "Test error: unexpected salt #3"); + assertTrue(address(info.auth) != address(0), "Test error: unexpected auth #3"); + assertTrue(info.auth.getRelease() == info.version, "Test error: unexpected auth version #2"); + assertTrue(info.auth.getServiceDomains().length > 0, "Test error: unexpected auth domain num #2"); + assertTrue(gteTimestamp(TimestampLib.blockTimestamp(), info.activatedAt), "Test error: unexpected activatedAt #3"); + assertTrue(info.disabledAt == TimestampLib.max(), "Test error: unexpected disabledAt #3"); + } else if (info.state == PAUSED()) { + assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #4"); + assertTrue(info.salt != bytes32(0), "Test error: unexpected salt #4"); + assertTrue(address(info.auth) != address(0), "Test error: unexpected auth #4"); + assertTrue(info.auth.getRelease() == info.version, "Test error: unexpected auth version #3"); + assertTrue(info.auth.getServiceDomains().length > 0, "Test error: unexpected auth domain num #3"); + assertTrue(gteTimestamp(TimestampLib.blockTimestamp(), info.activatedAt), "Test error: unexpected activatedAt #4"); + assertTrue(gteTimestamp(TimestampLib.blockTimestamp(), info.disabledAt), "Test error: unexpected disabledAt #4"); + assertTrue(gteTimestamp(info.disabledAt, info.activatedAt), "Test error: disabledAt < activatedAt #4"); + } else if (info.state == SKIPPED()) { + assertTrue(info.version.toInt() >= 3, "Test error: unexpected version #5"); + // salt can have any values + if(address(info.auth) != address(0)) { + assertTrue(info.auth.getRelease() == info.version, "Test error: unexpected auth version #4"); + assertTrue(info.auth.getServiceDomains().length > 0, "Test error: unexpected auth domain num #4"); + } + assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #5"); + assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #5"); + } else if (info.state == StateIdLib.zero()) { + assertTrue(info.version.toInt() == 0, "Test error: unexpected version #6"); + assertTrue(info.salt == bytes32(0), "Test error: unexpected salt #6"); + assertTrue(address(info.auth) == address(0), "Test error: unexpected auth #6"); + assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #6"); + assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #6"); + } else { + // solhint-disable-next-line + console.log("Unexpected state ", info.state.toInt()); + assertTrue(false, "Test error: unexpected state"); + } } } From 6f2d241f5ade39a6f0d2c7c531517f4deb147bb3 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Sun, 1 Sep 2024 12:58:11 +0000 Subject: [PATCH 10/18] cleanups --- contracts/authorization/AccessAdmin.sol | 105 +-------- contracts/authorization/AccessAdminLib.sol | 4 +- contracts/authorization/Authorization.sol | 117 ++-------- .../distribution/DistributionService.sol | 4 +- .../distribution/IDistributionComponent.sol | 11 +- contracts/instance/InstanceAdmin.sol | 216 +----------------- contracts/instance/InstanceReader.sol | 38 +-- contracts/instance/InstanceService.sol | 26 +-- contracts/registry/RegistryAdmin.sol | 107 +-------- contracts/registry/ReleaseAdmin.sol | 120 ++-------- contracts/shared/IService.sol | 1 - contracts/shared/Service.sol | 9 +- scripts/deploy_fire_components.ts | 2 +- test/base/GifTest.sol | 47 ---- .../authorization/InstanceAuthorization.t.sol | 9 - test/registry/RegistryTestBase.sol | 17 -- .../RegistryServiceHarnessTestBase.sol | 7 - test/release/ReleaseRegistryFuzz.t.sol | 52 ++--- 18 files changed, 85 insertions(+), 807 deletions(-) diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index f26f7e0aa..6d606715b 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -9,7 +9,6 @@ import {IAccess} from "./IAccess.sol"; import {IAccessAdmin} from "./IAccessAdmin.sol"; import {IAuthorization} from "./IAuthorization.sol"; import {IRegistry} from "../registry/IRegistry.sol"; -import {IService} from "../shared/IService.sol"; import {ADMIN_ROLE_NAME, PUBLIC_ROLE_NAME} from "./AccessAdmin.sol"; import {AccessAdminLib} from "./AccessAdminLib.sol"; @@ -23,10 +22,6 @@ import {Str, StrLib} from "../type/String.sol"; import {TimestampLib} from "../type/Timestamp.sol"; import {VersionPart} from "../type/Version.sol"; -interface IAccessManagedChecker { - function authority() external view returns (address); -} - function ADMIN_ROLE_NAME() pure returns (string memory) { return "AdminRole"; } function PUBLIC_ROLE_NAME() pure returns (string memory) { return "PublicRole"; } @@ -127,8 +122,6 @@ contract AccessAdmin is //-------------- initialization functions ------------------------------// - // event LogAccessAdminDebug(string message); - /// @dev Initializes this admin with the provided accessManager (and authorization specification). /// Internally initializes access manager with this admin and creates basic role setup. function initialize( @@ -184,6 +177,7 @@ contract AccessAdmin is _initializeAdminAndPublicRoles(); } + //--- view functions for access amdin ---------------------------------------// function getRelease() public view virtual returns (VersionPart release) { return _authority.getRelease(); @@ -282,12 +276,7 @@ contract AccessAdmin is return isMember; } - function isRoleAdmin(RoleId roleId, address account) - public - virtual - view - returns (bool) - { + function isRoleAdmin(RoleId roleId, address account) public virtual view returns (bool) { return isRoleMember(_roleInfo[roleId].adminRoleId, account); } @@ -354,6 +343,7 @@ contract AccessAdmin is _linkedNftId = getRegistry().getNftIdForAddress(registerable); } + function _initializeAdminAndPublicRoles() internal virtual @@ -382,37 +372,20 @@ contract AccessAdmin is name: PUBLIC_ROLE_NAME()})); } - // TODO cleanup - // function _createTargetWithRole( - // address target, - // string memory targetName, - // RoleId targetRoleId - // ) - // internal - // { - // _createTarget(target, targetName, true, false); - // _grantRoleToAccount(targetRoleId, target); - // } - - // TODO refactor / cleanup -event LogAccessAdminDebugTarget(string name, address target, RoleId roleId, RoleId authorizedRoleId); function _authorizeFunctions(IAuthorization authorization, Str target, RoleId roleId) internal { - RoleId authorizedRoleId = _toAuthorizedRoleId(authorization, roleId); - address getTargetAddress = getTargetForName(target); -emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, authorizedRoleId); - _authorizeTargetFunctions( getTargetForName(target), - authorizedRoleId, + _toAuthorizedRoleId(authorization, roleId), authorization.getAuthorizedFunctions( target, roleId), true); } + function _toAuthorizedRoleId(IAuthorization authorization, RoleId roleId) internal returns (RoleId authorizedRoleId) @@ -438,6 +411,7 @@ emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, auth return authorizedRoleId = getRoleForName(roleName); } + function _authorizeTargetFunctions( address target, RoleId roleId, @@ -458,53 +432,6 @@ emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, auth addFunctions); // add functions } - // function _unauthorizeTargetFunctions( - // address target, - // FunctionInfo[] memory functions - // ) - // internal - // { - // _grantRoleAccessToFunctions( - // target, - // getAdminRole(), - // functions, - // false); // addFunctions - // } - - // function _processFunctionSelectors( - // address target, - // FunctionInfo[] memory functions, - // bool addFunctions - // ) - // internal - // onlyExistingTarget(target) - // returns ( - // bytes4[] memory functionSelectors, - // string[] memory functionNames - // ) - // { - // uint256 n = functions.length; - // functionSelectors = new bytes4[](n); - // functionNames = new string[](n); - // FunctionInfo memory func; - // Selector selector; - - // for (uint256 i = 0; i < n; i++) { - // func = functions[i]; - // selector = func.selector; - - // // add function selector to target selector set if not in set - // if (addFunctions) { SelectorSetLib.add(_targetFunctions[target], selector); } - // else { SelectorSetLib.remove(_targetFunctions[target], selector); } - - // // set function name - // _functionInfo[target][selector] = func; - - // // add bytes4 selector to function selector array - // functionSelectors[i] = selector.toBytes4(); - // functionNames[i] = func.name.toString(); - // } - // } /// @dev grant the specified role access to all functions in the provided selector list function _grantRoleAccessToFunctions( @@ -588,6 +515,7 @@ emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, auth emit LogAccessAdminRoleGranted(_adminName, account, _getRoleName(roleId)); } + /// @dev revoke the specified role from the provided account function _revokeRoleFromAccount(RoleId roleId, address account) internal @@ -715,6 +643,7 @@ emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, auth emit LogAccessAdminRoleCreated(_adminName, roleId, info.roleType, info.adminRoleId, info.name.toString()); } + /// @dev Creates a new target and a corresponding contract role. /// The function assigns the role to the target and logs the creation. function _createTargetUnchecked( @@ -854,22 +783,4 @@ emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, roleId, auth revert ErrorAccessAdminTargetNotCreated(target); } } - - // TODO cleanup - // function _checkIsRegistered( - // address registry, - // address target, - // ObjectType expectedType - // ) - // internal - // view - // { - // AccessAdminLib.checkIsRegistered(registry, target, expectedType); - // } - - // function _checkRegistry(address registry) internal view { - // if (!ContractLib.isRegistry(registry)) { - // revert ErrorAccessAdminNotRegistry(registry); - // } - // } } \ No newline at end of file diff --git a/contracts/authorization/AccessAdminLib.sol b/contracts/authorization/AccessAdminLib.sol index cb7b1ec5e..83dbe2ae7 100644 --- a/contracts/authorization/AccessAdminLib.sol +++ b/contracts/authorization/AccessAdminLib.sol @@ -253,7 +253,7 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB function getCustomRoleId(uint64 index) public - view + pure returns (RoleId customRoleId) { return RoleIdLib.toRoleId(CUSTOM_ROLE_MIN + index); @@ -334,7 +334,7 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB } - function toRoleName(string memory name) public view returns (string memory) { + function toRoleName(string memory name) public pure returns (string memory) { return string( abi.encodePacked( name, diff --git a/contracts/authorization/Authorization.sol b/contracts/authorization/Authorization.sol index 8236a0c03..bc82840c3 100644 --- a/contracts/authorization/Authorization.sol +++ b/contracts/authorization/Authorization.sol @@ -23,10 +23,8 @@ contract Authorization is uint64 public constant COMPONENT_ROLE_MIN = 110000; uint64 internal _nextGifContractRoleId; - // mapping(ObjectType domain => Str target) internal _serviceTarget; string internal _tokenHandlerName = "ComponentTh"; - Str internal _tokenHandlerTarget; @@ -77,12 +75,6 @@ contract Authorization is } - // TODO cleanup - // function roleExists(RoleId roleId) public view returns(bool exists) { - // return _roleInfo[roleId].roleType != RoleType.Undefined; - // } - - function getTokenHandlerName() public view returns(string memory) { return _tokenHandlerName; } @@ -103,19 +95,6 @@ contract Authorization is return target == _mainTarget || _targetExists[target]; } - // TODO cleanup - // function getTargetRole(Str target) public view returns(RoleId roleId) { - // return _targetRole[target]; - // } - - // function getAuthorizedRoles(Str target) external view returns(RoleId[] memory roleIds) { - // return _authorizedRoles[target]; - // } - - // function getAuthorizedFunctions(Str target, RoleId roleId) external view returns(IAccess.FunctionInfo[] memory authorizatedFunctions) { - // return _authorizedFunctions[target][roleId]; - // } - /// @dev Sets up the relevant service targets for the component. /// Overwrite this function for use case specific authorizations. // solhint-disable-next-line no-empty-blocks @@ -141,8 +120,20 @@ contract Authorization is // solhint-disable-next-line no-empty-blocks function _setupTargetAuthorizations() internal virtual {} - function _addGifTarget(string memory contractName) internal { + /// @dev Add a contract role for the provided role id and name. + function _addCustomRole(RoleId roleId, RoleId adminRoleId, uint32 maxMemberCount, string memory name) internal { + _addRole( + roleId, + _toRoleInfo( + adminRoleId, + RoleType.Custom, + maxMemberCount, + name)); + } + + /// @dev Add a gif target with its corresponding contract role + function _addGifTarget(string memory contractName) internal { RoleId contractRoleId = RoleIdLib.toRoleId(_nextGifContractRoleId++); string memory contractRoleName = string( abi.encodePacked( @@ -155,81 +146,14 @@ contract Authorization is contractRoleName); } - // TODO cleanup - // /// @dev Add the service target role for the specified service domain - // function _addServiceTargetWithRole(ObjectType serviceDomain) internal { - // // add service domain - // _serviceDomains.push(serviceDomain); - - // // get versioned target name - // string memory serviceTargetName = ObjectTypeLib.toVersionedName( - // ObjectTypeLib.toName(serviceDomain), - // "Service", - // getRelease()); - - // // _serviceTarget[serviceDomain] = StrLib.toStr(serviceTargetName); - - // RoleId serviceRoleId = getServiceRole(serviceDomain); - // string memory serviceRoleName = ObjectTypeLib.toVersionedName( - // ObjectTypeLib.toName(serviceDomain), - // "ServiceRole", - // getRelease()); - - // _addTargetWithRole( - // serviceTargetName, - // serviceRoleId, - // serviceRoleName); - // } - - - // /// @dev Use this method to to add an authorized role. - // function _addRole(RoleId roleId, RoleInfo memory info) internal { - // _roles.push(roleId); - // _roleInfo[roleId] = info; - // } - - - // /// @dev Add a contract role for the provided role id and name. - // function _addContractRole(RoleId roleId, string memory name) internal { - // _addRole( - // roleId, - // _toRoleInfo( - // ADMIN_ROLE(), - // RoleType.Contract, - // 1, - // name)); - // } - - - /// @dev Add a contract role for the provided role id and name. - function _addCustomRole(RoleId roleId, RoleId adminRoleId, uint32 maxMemberCount, string memory name) internal { - _addRole( - roleId, - _toRoleInfo( - adminRoleId, - RoleType.Custom, - maxMemberCount, - name)); - } - /// @dev Use this method to to add an authorized target. function _addTarget(string memory name) internal { _addTargetWithRole(name, RoleIdLib.zero(), ""); } - // TODO cleanup - // /// @dev Use this method to authorize a specific function authorization - // function _authorize(IAccess.FunctionInfo[] storage functions, bytes4 selector, string memory name) internal { - // functions.push( - // IAccess.FunctionInfo({ - // selector: SelectorLib.toSelector(selector), - // name: StrLib.toStr(name), - // createdAt: TimestampLib.blockTimestamp()})); - // } - - /// @dev role id for targets registry, staking and instance + /// @dev Role id for targets registry, staking and instance function _toTargetRoleId(ObjectType targetDomain) internal pure @@ -239,23 +163,12 @@ contract Authorization is } + /// @dev Returns the role name for the specified target name function _toTargetRoleName(string memory targetName) internal pure returns (string memory) { return string( abi.encodePacked( targetName, ROLE_NAME_SUFFIX)); } - - // TODO cleanup - // /// @dev creates a role info object from the provided parameters - // function _toRoleInfo(RoleId adminRoleId, RoleType roleType, uint32 maxMemberCount, string memory name) internal view returns (RoleInfo memory info) { - // return RoleInfo({ - // name: StrLib.toStr(name), - // adminRoleId: adminRoleId, - // roleType: roleType, - // maxMemberCount: maxMemberCount, - // createdAt: TimestampLib.blockTimestamp(), - // pausedAt: TimestampLib.max()}); - // } } diff --git a/contracts/distribution/DistributionService.sol b/contracts/distribution/DistributionService.sol index ce1e5f354..1ffe2866f 100644 --- a/contracts/distribution/DistributionService.sol +++ b/contracts/distribution/DistributionService.sol @@ -13,8 +13,6 @@ import {IComponents} from "../instance/module/IComponents.sol"; import {IPolicy} from "../instance/module/IPolicy.sol"; import {Amount, AmountLib} from "../type/Amount.sol"; -// TODO cleanup -// import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol"; import {DistributorType, DistributorTypeLib} from "../type/DistributorType.sol"; import {NftId, NftIdLib} from "../type/NftId.sol"; import {KEEP_STATE} from "../type/StateId.sol"; @@ -411,12 +409,12 @@ contract DistributionService is } - // TODO cleanup function _getInstanceForDistribution(IRegistry registry, NftId distributionNftId) internal view returns(IInstance instance) { + // TODO refactor to ComponentLib or similar return PoolLib.getInstanceForComponent(registry, distributionNftId); } diff --git a/contracts/distribution/IDistributionComponent.sol b/contracts/distribution/IDistributionComponent.sol index cbbaee3ef..e300c47df 100644 --- a/contracts/distribution/IDistributionComponent.sol +++ b/contracts/distribution/IDistributionComponent.sol @@ -1,23 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {Amount} from "../type/Amount.sol"; import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol"; + +import {Amount} from "../type/Amount.sol"; import {NftId} from "../type/NftId.sol"; import {ReferralId, ReferralStatus} from "../type/Referral.sol"; import {UFixed} from "../type/UFixed.sol"; + interface IDistributionComponent is IInstanceLinkedComponent { event LogDistributorUpdated(address to, address operator); - // TODO cleanup - // /// @dev Returns true iff the provided address is registered as a distributor with this distribution component. - // function isDistributor(address candidate) external view returns (bool); - - // /// @dev Returns the distributor Nft Id for the provided address - // function getDistributorNftId(address distributor) external view returns (NftId distributorNftId); - function getDiscountPercentage( string memory referralCode ) external view returns (UFixed discountPercentage, ReferralStatus status); diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index 73d659992..ed00c1200 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -102,13 +102,9 @@ contract InstanceAdmin is // link nft ownability to instance _linkToNftOwnable(instance); - // TODO cleanup - // // create instance role and target - // _setupInstance(instance); - _setupServiceRoles(_authorization); - _createTargets(_authorization); + _createInstanceTargets(_authorization.getMainTargetName()); // add instance authorization _createRoles(_authorization); @@ -150,16 +146,10 @@ contract InstanceAdmin is } - function _createTargets(IAuthorization authorization) + function _createInstanceTargets(string memory instanceTargetName) internal { - // TODO cleanup - // _createTargetWithRole(address(this), INSTANCE_ADMIN_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(INSTANCE_ADMIN_TARGET_NAME))); - // _createTargetWithRole(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(INSTANCE_STORE_TARGET_NAME))); - // _createTargetWithRole(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(BUNDLE_SET_TARGET_NAME))); - // _createTargetWithRole(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(RISK_SET_TARGET_NAME))); - - _createManagedTarget(address(_instance), authorization.getMainTargetName(), TargetType.Instance); + _createManagedTarget(address(_instance), instanceTargetName, TargetType.Instance); _createManagedTarget(address(this), INSTANCE_ADMIN_TARGET_NAME, TargetType.Instance); _createManagedTarget(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME, TargetType.Instance); _createManagedTarget(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME, TargetType.Instance); @@ -201,31 +191,6 @@ contract InstanceAdmin is return _release; } - // TODO cleanup - // // create instance role and target - // function _setupInstance(address instance) internal { - - // // create instance role - // RoleId instanceRoleId = _authorization.getTargetRole( - // _authorization.getMainTarget()); - - // _createRole( - // instanceRoleId, - // _authorization.getRoleInfo(instanceRoleId)); - - // // create instance target - // _createTarget( - // instance, - // _authorization.getMainTargetName(), - // true, // checkAuthority - // false); // custom - - // // assign instance role to instance - // _grantRoleToAccount( - // instanceRoleId, - // instance); - // } - /// @dev Creates a custom role. function createRole( @@ -296,15 +261,6 @@ contract InstanceAdmin is target, name, TargetType.Custom); - - // TODO cleanup - // if (targetRoleId != RoleIdLib.zero()) { - // if (getRoleInfo(targetRoleId).roleType != IAccess.RoleType.Custom) { - // revert ErrorInstanceAdminNotCustomRole(targetRoleId); - // } - - // _grantRoleToAccount(targetRoleId, target); - // } } @@ -354,100 +310,6 @@ contract InstanceAdmin is // ------------------- Internal functions ------------------- // - // TODO cleanup - // function _setupComponentAndTokenHandler( - // IInstanceLinkedComponent component, - // ObjectType componentType - // ) - // internal - // { - - // IAuthorization authorization = component.getAuthorization(); - // string memory targetName = authorization.getMainTargetName(); - - // // create component role and target - // RoleId componentRoleId = _createComponentRoleId(component, authorization); - - // // create component's target - // _createTarget( - // address(component), - // targetName, - // true, // checkAuthority - // false); // custom - - // // create component's token handler target if app - // if (componentType != ORACLE()) { - // NftId componentNftId = _registry.getNftIdForAddress(address(component)); - // address tokenHandler = address( - // _instance.getInstanceReader().getComponentInfo( - // componentNftId).tokenHandler); - - // _createTarget( - // tokenHandler, - // authorization.getTokenHandlerName(), - // true, - // false); - - // // token handler does not require its own role - // // token handler is not calling other components - // } - - // // assign component role to component - // _grantRoleToAccount( - // componentRoleId, - // address(component)); - // } - - - // function _createComponentRoleId( - // IInstanceLinkedComponent component, - // IAuthorization authorization - // ) - // internal - // returns (RoleId componentRoleId) - // { - // // checks - // // check component is not yet authorized - // if (_targetRoleId[address(component)].gtz()) { - // revert ErrorInstanceAdminAlreadyAuthorized(address(component)); - // } - - // // check generic component role - // RoleId genericComponentRoleId = authorization.getTargetRole( - // authorization.getMainTarget()); - - // if (!genericComponentRoleId.isComponentRole()) { - // revert ErrorInstanceAdminNotComponentRole(genericComponentRoleId); - // } - - // // check component role does not exist - // componentRoleId = toComponentRole( - // genericComponentRoleId, - // _components); - - // if (roleExists(componentRoleId)) { - // revert ErrorInstanceAdminRoleAlreadyExists(componentRoleId); - // } - - // // check role info - // IAccess.RoleInfo memory roleInfo = authorization.getRoleInfo( - // genericComponentRoleId); - - // if (roleInfo.roleType != IAccess.RoleType.Contract) { - // revert ErrorInstanceAdminRoleTypeNotContract( - // componentRoleId, - // roleInfo.roleType); - // } - - // // effects - // _targetRoleId[address(component)] = componentRoleId; - // _components++; - - // _createRole( - // componentRoleId, - // roleInfo); - // } - function _createRoles(IAuthorization authorization) internal @@ -482,27 +344,6 @@ contract InstanceAdmin is function _createTargetAuthorizations(IAuthorization authorization) internal { - // TODO cleanup - // Str[] memory targets = authorization.getTargets(); - // Str target; - - // for(uint256 i = 0; i < targets.length; i++) { - // target = targets[i]; - // RoleId[] memory authorizedRoles = authorization.getAuthorizedRoles(target); - // RoleId authorizedRole; - - // for(uint256 j = 0; j < authorizedRoles.length; j++) { - // authorizedRole = authorizedRoles[j]; - - // _authorizeTargetFunctions( - // getTargetForName(target), - // authorizedRole, - // authorization.getAuthorizedFunctions( - // target, - // authorizedRole), - // true); - // } - // } Str[] memory targets = authorization.getTargets(); Str target; @@ -515,55 +356,4 @@ contract InstanceAdmin is } } } - - // TODO cleanup - // function _checkAndCreateTargetWithRole( - // address target, - // string memory targetName - // ) - // internal - // { - // // check that target name is defined in authorization specification - // Str name = StrLib.toStr(targetName); - // if (!_authorization.targetExists(name)) { - // revert ErrorInstanceAdminExpectedTargetMissing(targetName); - // } - - // // create named target - // _createTarget( - // target, - // targetName, - // false, // check authority TODO check normal targets, don't check service targets (they share authority with release admin) - // false); - - // // assign target role if defined - // RoleId targetRoleId = _authorization.getTargetRole(name); - // if (targetRoleId != RoleIdLib.zero()) { - // _grantRoleToAccount(targetRoleId, target); - // } - // } - - // function _setupInstanceHelperTargetsWithRoles() - // internal - // { - - // // create module targets - // _checkAndCreateTargetWithRole(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME); - // _checkAndCreateTargetWithRole(address(_instance.getInstanceAdmin()), INSTANCE_ADMIN_TARGET_NAME); - // _checkAndCreateTargetWithRole(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME); - // _checkAndCreateTargetWithRole(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME); - - // // create targets for services that need to access the instance targets - // ObjectType[] memory serviceDomains = _authorization.getServiceDomains(); - // VersionPart release = _authorization.getRelease(); - // ObjectType serviceDomain; - - // for (uint256 i = 0; i < serviceDomains.length; i++) { - // serviceDomain = serviceDomains[i]; - - // _checkAndCreateTargetWithRole( - // _registry.getServiceAddress(serviceDomain, release), - // _authorization.getServiceTarget(serviceDomain).toString()); - // } - // } } diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index 3d5ebdce4..9555bab44 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -36,7 +36,8 @@ import {TokenHandler} from "../shared/TokenHandler.sol"; import {UFixed, UFixedLib} from "../type/UFixed.sol"; - +/// @dev Central reader contract for a specific instance. +/// Should provide convenient reading functions for all instance and related component data. contract InstanceReader { error ErrorInstanceReaderAlreadyInitialized(); @@ -191,14 +192,6 @@ contract InstanceReader { view returns (bool isCloseable) { - // TODO cleanup - // IPolicy.PolicyInfo memory info = getPolicyInfo(policyNftId); - - // if (info.productNftId.eqz()) { return false; } // not closeable: policy does not exist (or does not belong to this instance) - // if (info.activatedAt.eqz()) { return false; } // not closeable: not yet activated - // if (info.activatedAt > TimestampLib.blockTimestamp()) { return false; } // not yet active - // if (info.expiredAt <= TimestampLib.blockTimestamp()) { return false; } // already expired - return PolicyServiceLib.policyIsActive(this, policyNftId); } @@ -559,33 +552,6 @@ contract InstanceReader { _instance.getRelease())).getDiscountPercentage( this, // instance reader referralId); - - // TODO cleanup - // IDistribution.ReferralInfo memory info = getReferralInfo( - // referralId); - - // if (info.expiryAt.eqz()) { - // return ( - // UFixedLib.zero(), - // REFERRAL_ERROR_UNKNOWN()); - // } - - // if (info.expiryAt < TimestampLib.blockTimestamp()) { - // return ( - // UFixedLib.zero(), - // REFERRAL_ERROR_EXPIRED()); - // } - - // if (info.usedReferrals >= info.maxReferrals) { - // return ( - // UFixedLib.zero(), - // REFERRAL_ERROR_EXHAUSTED()); - // } - - // return ( - // info.discountPercentage, - // REFERRAL_OK() - // ); } diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index fa81650be..a032311ae 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -56,23 +56,6 @@ contract InstanceService is } - // TODO cleanup - // modifier onlyInstanceOwner(NftId instanceNftId) { - // if(msg.sender != getRegistry().ownerOf(instanceNftId)) { - // revert ErrorInstanceServiceRequestUnauhorized(msg.sender); - // } - // _; - // } - - // // TODO check component - service - instance version match - // modifier onlyComponent() { - // if (! getRegistry().isRegisteredComponent(msg.sender)) { - // revert ErrorInstanceServiceRequestUnauhorized(msg.sender); - // } - // _; - // } - - /// @inheritdoc IInstanceService function createRole( string memory roleName, @@ -124,6 +107,7 @@ contract InstanceService is instance.getInstanceAdmin().revokeRole(roleId, account); } + /// @inheritdoc IInstanceService function createTarget(address target, string memory name) external @@ -339,6 +323,7 @@ contract InstanceService is masterInstanceNftId = info.nftId; } + function upgradeMasterInstanceReader(address instanceReaderAddress) external onlyOwner @@ -353,10 +338,14 @@ contract InstanceService is _masterInstanceReader = instanceReaderAddress; } + //--- view functions ------------------------------------------------------------// + function getMasterInstanceReader() external view returns (address) { return _masterInstanceReader; } + //--- internal functions --------------------------------------------------------// + /// @dev create new cloned instance admin /// function used to setup a new instance function _cloneNewInstanceAdmin() @@ -435,7 +424,8 @@ contract InstanceService is // instanceAdmin.setTargetFunctionRoleByService(targetName, selectors[roleIdx], roles[roleIdx]); } } - + + /// @dev top level initializer function _initialize( address owner, diff --git a/contracts/registry/RegistryAdmin.sol b/contracts/registry/RegistryAdmin.sol index 11fd6dd4c..178a01915 100644 --- a/contracts/registry/RegistryAdmin.sol +++ b/contracts/registry/RegistryAdmin.sol @@ -45,16 +45,6 @@ contract RegistryAdmin is string public constant GIF_ADMIN_ROLE_NAME = "GifAdminRole"; string public constant GIF_MANAGER_ROLE_NAME = "GifManagerRole"; - // cleanup - // string public constant RELEASE_REGISTRY_ROLE_NAME = "ReleaseRegistryRole"; - // string public constant STAKING_ROLE_NAME = "StakingRole"; - - /// @dev gif roles for external contracts - // string public constant REGISTRY_SERVICE_ROLE_NAME = "RegistryServiceRole"; - // string public constant COMPONENT_SERVICE_ROLE_NAME = "ComponentServiceRole"; - // string public constant POOL_SERVICE_ROLE_NAME = "PoolServiceRole"; - // string public constant STAKING_SERVICE_ROLE_NAME = "StakingServiceRole"; - /// @dev gif core targets string public constant REGISTRY_ADMIN_TARGET_NAME = "RegistryAdmin"; string public constant REGISTRY_TARGET_NAME = "Registry"; @@ -116,10 +106,8 @@ contract RegistryAdmin is // link nft ownability to registry _linkToNftOwnable(_registry); - // _setupRegistry(_registry); - - // TODO check if targets should be registerd first - _createTargets(_authorization); + // register core targets + _createCoreTargets(_authorization.getMainTargetName()); // setup authorization for registry and supporting contracts _createRoles(_authorization); @@ -154,34 +142,6 @@ contract RegistryAdmin is //--- private initialization functions -------------------------------------------// - // TODO cleanup - // // create registry role and target - // function _setupRegistry(address registry) internal { - - // // create registry role - // RoleId roleId = _authorization.getTargetRole( - // _authorization.getMainTarget()); - - // _createRole( - // roleId, - // _authorization.getRoleInfo(roleId)); - - // // create registry target - // _createTarget( - // registry, - // _authorization.getMainTargetName(), - // true, // checkAuthority - // false); // custom - - // // assign registry role to registry - // _grantRoleToAccount( - // roleId, - // registry); - // } - - // TODO cleanup - event LogAccessAdminDebugRole(string name, RoleId roleId, bool exists); - function _createRoles(IAuthorization authorization) internal { @@ -192,11 +152,7 @@ contract RegistryAdmin is RoleInfo memory roleInfo = authorization.getRoleInfo(roleId); // create role if not exists - bool exists = roleExists(roleInfo.name); - -emit LogAccessAdminDebugRole(roleInfo.name.toString(), roleId, exists); - - if (!exists) { + if (!roleExists(roleInfo.name)) { _createRole( roleId, roleInfo); @@ -205,29 +161,20 @@ emit LogAccessAdminDebugRole(roleInfo.name.toString(), roleId, exists); } - function _createTargets(IAuthorization authorization) + function _createCoreTargets(string memory registryTargetName) internal { - // TODO cleanup // registry - // _createTargetWithRole(address(this), REGISTRY_ADMIN_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(REGISTRY_ADMIN_TARGET_NAME))); - // _createTargetWithRole(_releaseRegistry, RELEASE_REGISTRY_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(RELEASE_REGISTRY_TARGET_NAME))); - // _createTargetWithRole(_tokenRegistry, TOKEN_REGISTRY_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(TOKEN_REGISTRY_TARGET_NAME))); - - _createUncheckedTarget(_registry, authorization.getMainTargetName(), TargetType.Core); + _createUncheckedTarget(_registry, registryTargetName, TargetType.Core); _createUncheckedTarget(address(this), REGISTRY_ADMIN_TARGET_NAME, TargetType.Core); _createUncheckedTarget(_releaseRegistry, RELEASE_REGISTRY_TARGET_NAME, TargetType.Core); _createUncheckedTarget(_tokenRegistry, TOKEN_REGISTRY_TARGET_NAME, TargetType.Core); // staking - // _createTargetWithRole(_staking, STAKING_TARGET_NAME, authorization.getTargetRole(StrLib.toStr(STAKING_TARGET_NAME))); - // _createTarget(_stakingStore, STAKING_STORE_TARGET_NAME, true, false); - // _createTarget(address(IComponent(_staking).getTokenHandler()), STAKING_TH_TARGET_NAME, true, false); _createUncheckedTarget(_staking, STAKING_TARGET_NAME, TargetType.Core); _createUncheckedTarget(_stakingStore, STAKING_STORE_TARGET_NAME, TargetType.Core); } - // TODO cleanup function _createTargetAuthorizations(IAuthorization authorization) internal @@ -244,48 +191,4 @@ emit LogAccessAdminDebugRole(roleInfo.name.toString(), roleId, exists); } } } - - // TODO cleanup -// event LogAccessAdminDebugTarget(string name, address target, RoleId roleId); -// function _authorizeFunctions(IAuthorization authorization, RoleId roleId) -// internal -// { -// RoleId authorizedRoleId = _getAuthorizedRoleId(authorization, roleId); -// address getTargetAddress = getTargetForName(target); -// emit LogAccessAdminDebugTarget(target.toString(), getTargetAddress, authorizedRoleId); - -// _authorizeTargetFunctions( -// getTargetForName(target), -// authorizedRoleId, -// authorization.getAuthorizedFunctions( -// target, -// roleId), -// true); -// } - -// function _getAuthorizedRoleId(IAuthorization authorization, RoleId roleId) -// internal -// view -// returns (RoleId authorizedRoleId) -// { -// string memory roleName = authorization.getRoleInfo(roleId).name.toString(); -// return authorizedRoleId = getRoleForName(roleName); -// } - - // TODO cleanup - // function _createTargets(address authorization) - // private - // onlyInitializing() - // { - // IStaking staking = IStaking(_staking); - // address tokenHandler = address(staking.getTokenHandler()); - - // _createTarget(address(this), REGISTRY_ADMIN_TARGET_NAME, false, false, false); - // _createTarget(_registry, REGISTRY_TARGET_NAME, true, false, false); - // _createTarget(_releaseRegistry, RELEASE_REGISTRY_TARGET_NAME, true, false); - // _createTarget(_staking, STAKING_TARGET_NAME, true, false); - // _createTarget(_stakingStore, STAKING_STORE_TARGET_NAME, true, false); - // _createTarget(_tokenRegistry, TOKEN_REGISTRY_TARGET_NAME, true, false); - // _createTarget(tokenHandler, TOKEN_HANDLER_TARGET_NAME, true, false); - // } } \ No newline at end of file diff --git a/contracts/registry/ReleaseAdmin.sol b/contracts/registry/ReleaseAdmin.sol index a55f3387c..9e139abf3 100644 --- a/contracts/registry/ReleaseAdmin.sol +++ b/contracts/registry/ReleaseAdmin.sol @@ -9,11 +9,12 @@ import {IServiceAuthorization} from "../authorization/IServiceAuthorization.sol" import {AccessAdmin} from "../authorization/AccessAdmin.sol"; import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; -import {ObjectType, ObjectTypeLib, ALL} from "../type/ObjectType.sol"; -import {RoleId, RoleIdLib, ADMIN_ROLE, RELEASE_REGISTRY_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; -import {Str, StrLib} from "../type/String.sol"; +import {ObjectType, ObjectTypeLib} from "../type/ObjectType.sol"; +import {RoleId, ADMIN_ROLE, RELEASE_REGISTRY_ROLE} from "../type/RoleId.sol"; +import {Str} from "../type/String.sol"; import {VersionPart} from "../type/Version.sol"; + /// @dev The ReleaseAdmin contract implements the central authorization for the services of a specific release. /// There is one ReleaseAdmin contract per major GIF release. /// Locking/unlocking of all services of a release is implemented in function setReleaseLocked. @@ -42,6 +43,7 @@ contract ReleaseAdmin is _; } + // @dev Only used for master release admin constructor(address accessManager) { initialize( @@ -73,6 +75,7 @@ contract ReleaseAdmin is _setupReleaseRegistry(releaseRegistry); } + /// @dev Sets up authorizaion for specified service. /// For all authorized services its authorized functions are enabled. /// Permissioned function: Access is restricted to release registry. @@ -86,9 +89,20 @@ contract ReleaseAdmin is restricted() { _createServiceTargetAndRole(service, serviceDomain, release); - _authorizeServiceFunctions(serviceAuthorization, service, serviceDomain, release); + + // authorize functions of service + Str serviceTarget = serviceAuthorization.getServiceTarget(serviceDomain); + RoleId[] memory authorizedRoles = serviceAuthorization.getAuthorizedRoles(serviceTarget); + + for (uint256 i = 0; i < authorizedRoles.length; i++) { + _authorizeFunctions( + IAuthorization(address(serviceAuthorization)), + serviceTarget, + authorizedRoles[i]); + } } + /// @dev Locks/unlocks all release targets. /// For all authorized services of release its authorized functions are disabled/enabled. /// Permissioned function: Access is restricted to release registry. @@ -109,6 +123,7 @@ contract ReleaseAdmin is emit LogReleaseAdminReleaseLockChanged(getRelease(), locked); } + /// @dev Lock/unlock specific service of release. /// Permissioned function: Access is restricted to release registry. function setServiceLocked(IService service, bool locked) @@ -126,13 +141,6 @@ contract ReleaseAdmin is emit LogReleaseAdminServiceLockChanged(service.getRelease(), address(service), locked); } - //--- view functions ----------------------------------------------------// - -/* - function getReleaseAdminRole() external view returns (RoleId) { - return GIF_ADMIN_ROLE(); - } -*/ //--- private functions -------------------------------------------------// function _createServiceTargetAndRole( @@ -149,100 +157,10 @@ contract ReleaseAdmin is baseName, "Service", release); _createUncheckedTarget(address(service), serviceTargetName, TargetType.Service); - - // _createManagedTarget( - // address(service), - // serviceTargetName, - // IAccess.TargetType.Service); - - // // create service role - // RoleId serviceRoleId = RoleIdLib.toServiceRoleId( - // serviceDomain, - // release); - - // if(!roleExists(serviceRoleId)) { - // _createRole( - // serviceRoleId, - // AccessAdminLib.toRole({ - // adminRoleId: ADMIN_ROLE(), - // roleType: RoleType.Contract, - // maxMemberCount: 1, - // name: ObjectTypeLib.toVersionedName( - // baseName, - // "ServiceRole", - // release)})); - // } - - // _grantRoleToAccount( - // serviceRoleId, - // address(service)); - } - - - function _authorizeServiceFunctions( - IServiceAuthorization serviceAuthorization, - IService service, - ObjectType serviceDomain, - VersionPart release - ) - private - { - ObjectType authorizedDomain; - RoleId authorizedRoleId; - - Str serviceTarget = serviceAuthorization.getServiceTarget(serviceDomain); - RoleId[] memory authorizedRoles = serviceAuthorization.getAuthorizedRoles(serviceTarget); - - for (uint256 i = 0; i < authorizedRoles.length; i++) { - // authorizedDomain = authorizedDomains[i]; - - // // derive authorized role from authorized domain - // if (authorizedDomain == ALL()) { - // authorizedRoleId = PUBLIC_ROLE(); - // } else { - // authorizedRoleId = RoleIdLib.toServiceRoleId( - // authorizedDomain, - // release); - // } - - // Str target = StrLib.toStr(ObjectTypeLib.toName(service.getDomain())); - - _authorizeFunctions( - IAuthorization(address(serviceAuthorization)), - serviceTarget, - authorizedRoles[i]); - - // TODO cleanup - // if(!roleExists(authorizedRoleId)) { - // // create role for authorized domain - // _createRole( - // authorizedRoleId, - // AccessAdminLib.toRole({ - // adminRoleId: ADMIN_ROLE(), - // roleType: RoleType.Contract, - // maxMemberCount: 1, - // name: ObjectTypeLib.toVersionedName( - // ObjectTypeLib.toName(authorizedDomain), - // "Role", - // release)})); - // } - - // // get authorized functions for authorized domain - // IAccess.FunctionInfo[] memory authorizatedFunctions = serviceAuthorization.getAuthorizedFunctions( - // serviceDomain, - // authorizedDomain); - - // _authorizeTargetFunctions( - // address(service), - // authorizedRoleId, - // authorizatedFunctions, - // true); - } } //--- private initialization functions -------------------------------------------// - // TODO check if this can be moved to a standard Authorization setup function _setupReleaseRegistry(address releaseRegistry) private onlyInitializing() diff --git a/contracts/shared/IService.sol b/contracts/shared/IService.sol index be13f5ebd..ab6d485b9 100644 --- a/contracts/shared/IService.sol +++ b/contracts/shared/IService.sol @@ -17,7 +17,6 @@ interface IService is /// In any GIF release only one service for any given domain may be deployed. function getDomain() external pure returns(ObjectType serviceDomain); - // TODO cleanup /// @dev returns the GIF release specific role id. /// These role ids are used to authorize service to service communication. function getRoleId() external view returns(RoleId serviceRoleId); diff --git a/contracts/shared/Service.sol b/contracts/shared/Service.sol index 1539a703b..f937f85c1 100644 --- a/contracts/shared/Service.sol +++ b/contracts/shared/Service.sol @@ -1,16 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import {IRegistry} from "../registry/IRegistry.sol"; import {IService} from "./IService.sol"; import {IVersionable} from "../upgradeability/IVersionable.sol"; -import {ObjectType, ObjectTypeLib, REGISTRY, SERVICE} from "../type/ObjectType.sol"; +import {ObjectType, SERVICE} from "../type/ObjectType.sol"; import {Registerable} from "./Registerable.sol"; import {RoleId, RoleIdLib} from "../type/RoleId.sol"; -import {Version, VersionLib, VersionPartLib} from "../type/Version.sol"; +import {Version, VersionLib} from "../type/Version.sol"; import {Versionable} from "../upgradeability/Versionable.sol"; @@ -53,13 +52,15 @@ abstract contract Service is return VersionLib.toVersion(3, 0, 0); } - // TODO cleanup + function getRoleId() external virtual view returns(RoleId serviceRoleId) { return RoleIdLib.toServiceRoleId(_getDomain(), getRelease()); } + //--- internal functions --------------------------------------------------------// function _getDomain() internal virtual pure returns (ObjectType); + function _getServiceAddress(ObjectType domain) internal view returns (address) { return getRegistry().getServiceAddress(domain, getRelease()); } diff --git a/scripts/deploy_fire_components.ts b/scripts/deploy_fire_components.ts index c8210ffd5..e37a9eed1 100644 --- a/scripts/deploy_fire_components.ts +++ b/scripts/deploy_fire_components.ts @@ -1,5 +1,5 @@ import { resolveAddress, Signer } from "ethers"; -import { FirePool, FireProduct, FireProduct__factory, IInstance__factory, IInstanceService__factory, IRegistry__factory, TokenRegistry__factory } from "../typechain-types"; +import { FirePool, FireProduct, FireProduct__factory, AccessAdmin__factory, IInstance__factory, IInstanceService__factory, IRegistry__factory, TokenRegistry__factory } from "../typechain-types"; import { getNamedAccounts } from "./libs/accounts"; import { deployContract } from "./libs/deployment"; import { LibraryAddresses } from "./libs/libraries"; diff --git a/test/base/GifTest.sol b/test/base/GifTest.sol index 4984a77f7..74dc2e6c9 100644 --- a/test/base/GifTest.sol +++ b/test/base/GifTest.sol @@ -223,53 +223,6 @@ contract GifTest is GifDeployer { console.log(message, gasDelta); } - // TODO cleanup - // function _deployCore( - // address gifAdmin, - // address gifManager - // ) - // internal - // { - // ( - // dip, - // registry, - // tokenRegistry, - // releaseRegistry, - // registryAdmin, - // stakingManager, - // staking - // ) = deployCore( - // globalRegistry, - // gifAdmin, - // gifManager, - // stakingOwner); - - // // obtain some references - // registryAddress = address(registry); - // chainNft = ChainNft(registry.getChainNftAddress()); - // registryNftId = registry.getNftIdForAddress(registryAddress); - // stakingNftId = registry.getNftIdForAddress(address(staking)); - // stakingReader = staking.getStakingReader(); - - // // solhint-disable - // console.log("registry deployed at", address(registry)); - // console.log("registry owner", registryOwner); - - // console.log("token registry deployed at", address(tokenRegistry)); - // console.log("release manager deployed at", address(releaseRegistry)); - - // console.log("registry access manager deployed:", address(registryAdmin)); - // console.log("registry access manager authority", registryAdmin.authority()); - - // console.log("staking manager deployed at", address(stakingManager)); - - // console.log("staking nft id", registry.getNftIdForAddress(address(staking)).toInt()); - // console.log("staking deployed at", address(staking)); - // console.log("staking owner (opt 1)", registry.ownerOf(address(staking))); - // console.log("staking owner (opt 2)", staking.getOwner()); - // // solhint-enable - // } - function _deployAndRegisterServices( address gifAdmin, diff --git a/test/instance/authorization/InstanceAuthorization.t.sol b/test/instance/authorization/InstanceAuthorization.t.sol index 86e715c1d..9c51bf3b6 100644 --- a/test/instance/authorization/InstanceAuthorization.t.sol +++ b/test/instance/authorization/InstanceAuthorization.t.sol @@ -1,19 +1,10 @@ // SPDX-License-Identifier: APACHE-2.0 pragma solidity ^0.8.20; -// TODO cleanup imports -// import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; import {Test, console} from "../../../lib/forge-std/src/Test.sol"; import {InstanceAuthorizationV3} from "../../../contracts/instance/InstanceAuthorizationV3.sol"; -// import {Amount, AmountLib} from "../../../contracts/type/Amount.sol"; -// import {Fee, FeeLib} from "../../../contracts/type/Fee.sol"; -// import {GifTest} from "../../base/GifTest.sol"; -// import {InstanceAdmin} from "../../../contracts/instance/InstanceAdmin.sol"; -// import {InstanceReader} from "../../../contracts/instance/InstanceReader.sol"; -// import {NftId} from "../../../contracts/type/NftId.sol"; -// import {UFixed, UFixedLib} from "../../../contracts/type/UFixed.sol"; contract InstanceAuthorizationV3Instance is Test { diff --git a/test/registry/RegistryTestBase.sol b/test/registry/RegistryTestBase.sol index 5777b5f1b..250022264 100644 --- a/test/registry/RegistryTestBase.sol +++ b/test/registry/RegistryTestBase.sol @@ -55,33 +55,16 @@ contract RegistryTestBase is GifDeployer, FoundryRandom { RegistryServiceManagerMock public registryServiceManagerMock; RegistryServiceMock public registryServiceMock; - // TODO cleanup - // IERC20Metadata public dip; - // address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet - // address public registryOwner = makeAddr("registryOwner"); address public outsider = makeAddr("outsider"); - // address public gifAdmin = registryOwner; - // address public gifManager = registryOwner; - // address public stakingOwner = registryOwner; AccessManager public accessManager; - // RegistryAdmin public registryAdmin; - // Registry public registry; - // ChainNft public chainNft; - // ReleaseRegistry public releaseRegistry; - // TokenRegistry public tokenRegistry; - // StakingManager public stakingManager; - // Staking public staking; StakingStore public stakingStore; - // StakingReader public stakingReader; address public _sender; // use with _startPrank(), _stopPrank() uint public _nextId; // use with chainNft.calculateId() NftId public protocolNftId = NftIdLib.toNftId(1101); NftId public globalRegistryNftId = NftIdLib.toNftId(2101); - // NftId public registryNftId; // chainId dependent - // NftId public stakingNftId; // chainId dependent IRegistry.ObjectInfo public protocolInfo; IRegistry.ObjectInfo public globalRegistryInfo; // chainId dependent diff --git a/test/registryService/RegistryServiceHarnessTestBase.sol b/test/registryService/RegistryServiceHarnessTestBase.sol index ba1837b33..5c7e008a4 100644 --- a/test/registryService/RegistryServiceHarnessTestBase.sol +++ b/test/registryService/RegistryServiceHarnessTestBase.sol @@ -32,14 +32,7 @@ import {GifTest} from "../base/GifTest.sol"; contract RegistryServiceHarnessTestBase is GifDeployer, FoundryRandom { address public registerableOwner = makeAddr("registerableOwner"); - - // TODO cleanup - // IRegistry public registry; - // address public registryAddress; - // address public globalRegistry = makeAddr("globalRegistry"); - // address public registryOwner = makeAddr("registryOwner"); address public outsider = makeAddr("outsider"); - // ReleaseRegistry releaseRegistry; RegistryServiceManagerMockWithHarness public registryServiceManagerWithHarness; RegistryServiceHarness public registryServiceHarness; diff --git a/test/release/ReleaseRegistryFuzz.t.sol b/test/release/ReleaseRegistryFuzz.t.sol index c1fe840d0..da0f7c0ec 100644 --- a/test/release/ReleaseRegistryFuzz.t.sol +++ b/test/release/ReleaseRegistryFuzz.t.sol @@ -1,35 +1,23 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol"; -import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; import {FoundryRandom} from "foundry-random/FoundryRandom.sol"; import {console} from "../../lib/forge-std/src/Test.sol"; -import {VersionPart, VersionPartLib} from "../../contracts/type/Version.sol"; -import {StateIdLib, SCHEDULED, DEPLOYING, DEPLOYED, SKIPPED, ACTIVE, PAUSED} from "../../contracts/type/StateId.sol"; -import {TimestampLib, Timestamp, gteTimestamp} from "../../contracts/type/Timestamp.sol"; -import {NftId, NftIdLib} from "../../contracts/type/NftId.sol"; -import {RoleId, RoleIdLib} from "../../contracts/type/RoleId.sol"; -import {ObjectType, RELEASE, REGISTRY, SERVICE, PRODUCT} from "../../contracts/type/ObjectType.sol"; - -import {ILifecycle} from "../../contracts/shared/Lifecycle.sol"; -import {IService} from "../../contracts/shared/IService.sol"; - -import {IServiceAuthorization} from "../../contracts/authorization/IServiceAuthorization.sol"; - -import {RegistryAdmin} from "../../contracts/registry/RegistryAdmin.sol"; import {IRegistry} from "../../contracts/registry/IRegistry.sol"; import {IRelease} from "../../contracts/registry/IRelease.sol"; -import {ReleaseRegistry} from "../../contracts/registry/ReleaseRegistry.sol"; -import {ChainNft} from "../../contracts/registry/ChainNft.sol"; +import {IService} from "../../contracts/shared/IService.sol"; +import {ChainNft} from "../../contracts/registry/ChainNft.sol"; import {GifDeployer} from "../base/GifDeployer.sol"; +import {NftIdLib} from "../../contracts/type/NftId.sol"; +import {ObjectType, SERVICE} from "../../contracts/type/ObjectType.sol"; +import {ReleaseRegistry} from "../../contracts/registry/ReleaseRegistry.sol"; +import {ServiceMockWithRegistryDomainV3, ServiceMockWithRegistryDomainV4, ServiceMockWithRegistryDomainV5} from "../mock/ServiceMock.sol"; +import {StateIdLib, SCHEDULED, DEPLOYING, DEPLOYED, SKIPPED, ACTIVE, PAUSED} from "../../contracts/type/StateId.sol"; +import {TimestampLib, gteTimestamp} from "../../contracts/type/Timestamp.sol"; +import {VersionPart, VersionPartLib} from "../../contracts/type/Version.sol"; -import {ServiceAuthorizationMock, ServiceAuthorizationMockWithRegistryService} from "../mock/ServiceAuthorizationMock.sol"; -import {NftOwnableMock} from "../mock/NftOwnableMock.sol"; -import {ServiceMock, ServiceMockWithRegistryDomainV3, ServiceMockWithRegistryDomainV4, ServiceMockWithRegistryDomainV5} from "../mock/ServiceMock.sol"; -import {Usdc} from "../mock/Usdc.sol"; contract ReleaseRegistryTest is GifDeployer, FoundryRandom { @@ -42,20 +30,8 @@ contract ReleaseRegistryTest is GifDeployer, FoundryRandom { // keep identical to IRegistry events event LogServiceRegistration(VersionPart majorVersion, ObjectType serviceDomain); - // TODO cleanup - // address public globalRegistry = makeAddr("globalRegistry"); // address of global registry when not on mainnet - // address public gifAdmin = makeAddr("gifAdmin"); - // address public gifManager = makeAddr("gifManager"); - // address public stakingOwner = makeAddr("stakingOwner"); address public outsider = makeAddr("outsider"); - - // RegistryAdmin registryAdmin; - // IRegistry registry; - // ChainNft chainNft; - // ReleaseRegistry releaseRegistry; - // NftId registryNftId; - - mapping(VersionPart version => IService) serviceByVersion; + mapping(VersionPart version => IService) public serviceByVersion; function setUp() public virtual @@ -80,7 +56,7 @@ contract ReleaseRegistryTest is GifDeployer, FoundryRandom { chainNft = ChainNft(registry.getChainNftAddress()); registryNftId = registry.getNftId(); - + vm.startPrank(gifManager); serviceByVersion[VersionPartLib.toVersionPart(3)] = new ServiceMockWithRegistryDomainV3( @@ -206,6 +182,7 @@ contract ReleaseRegistryTest is GifDeployer, FoundryRandom { assertTrue(info.activatedAt.eqz(), "Test error: unexpected activatedAt #6"); assertTrue(info.disabledAt.eqz(), "Test error: unexpected disabledAt #6"); } else { + // solhint-disable next-line console.log("Unexpected state ", info.state.toInt()); assertTrue(false, "Test error: unexpected state"); } @@ -224,7 +201,7 @@ contract ReleaseRegistryTest is GifDeployer, FoundryRandom { function test_releaseRegistry_setUp() public view { - for(uint i = 0; i <= releaseRegistry.INITIAL_GIF_VERSION() + 1; i++) { + for(uint256 i = 0; i <= releaseRegistry.INITIAL_GIF_VERSION() + 1; i++) { _assert_releaseRegistry_getters(VersionPartLib.toVersionPart(i), zeroReleaseInfo()); } @@ -238,9 +215,6 @@ contract ReleaseRegistryTest is GifDeployer, FoundryRandom { assertEq(address(releaseRegistry.getRegistry()), address(registry), "getRegistry() return unexpected value"); } - // TODO how to fuzz? can not parametrize service version and domain because of pure modifier - // TODO testFuzz_verifyServiceInfo() harness - // TODO testFuzz_releaseRegistry_registerService_whenReleaseDeploying(NftId nftId, NftId parentNftId, ObjectType objectType, address objectAddress, address initialOwner, bytes memory data, ObjectType domain, VersionPart version) public function testFuzz_releaseRegistry_prepareRelease_verifyServiceInfo() public { From 118ba5f86235bf1ba79088962509d8445916a07b Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Sun, 1 Sep 2024 15:17:43 +0000 Subject: [PATCH 11/18] unify *Admin.completeSetup() functions --- contracts/authorization/AccessAdmin.sol | 3 + contracts/authorization/AccessAdminLib.sol | 12 ++- contracts/authorization/IAccessAdmin.sol | 1 + contracts/instance/InstanceAdmin.sol | 26 +++--- contracts/instance/InstanceService.sol | 4 +- contracts/registry/RegistryAdmin.sol | 24 ++---- contracts/registry/ReleaseAdmin.sol | 25 ++++-- contracts/registry/ReleaseRegistry.sol | 15 ++-- contracts/registry/TokenRegistry.sol | 6 +- test/authorization/RegistryAdminEx.sol | 6 +- test/base/GifDeployer.sol | 99 ++++++++++++++++------ test/base/GifTest.sol | 8 +- 12 files changed, 142 insertions(+), 87 deletions(-) diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index 6d606715b..26ecb088c 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -373,6 +373,7 @@ contract AccessAdmin is } + /// @dev Authorize the functions of the target for the specified role. function _authorizeFunctions(IAuthorization authorization, Str target, RoleId roleId) internal { @@ -733,6 +734,7 @@ contract AccessAdmin is address authorization, ObjectType expectedDomain, VersionPart expectedRelease, + bool expectServiceAuthorization, bool checkAlreadyInitialized ) internal @@ -743,6 +745,7 @@ contract AccessAdmin is authorization, expectedDomain, expectedRelease, + expectServiceAuthorization, checkAlreadyInitialized); } diff --git a/contracts/authorization/AccessAdminLib.sol b/contracts/authorization/AccessAdminLib.sol index 83dbe2ae7..f639ffcd7 100644 --- a/contracts/authorization/AccessAdminLib.sol +++ b/contracts/authorization/AccessAdminLib.sol @@ -9,6 +9,7 @@ import {IAuthorization} from "./IAuthorization.sol"; import {IComponent} from "../shared/IComponent.sol"; import {IRegistry} from "../registry/IRegistry.sol"; import {IService} from "../shared/IService.sol"; +import {IServiceAuthorization} from "./IServiceAuthorization.sol"; import {ContractLib} from "../shared/ContractLib.sol"; import {ObjectType} from "../type/ObjectType.sol"; @@ -143,6 +144,7 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB address authorization, ObjectType expectedDomain, VersionPart expectedRelease, + bool expectServiceAuthorization, bool checkAlreadyInitialized ) public @@ -155,8 +157,14 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB } // check contract type matches - if (!ContractLib.supportsInterface(authorization, type(IAuthorization).interfaceId)) { - revert IAccessAdmin.ErrorAccessAdminNotAuthorization(authorization); + if (expectServiceAuthorization) { + if (!ContractLib.supportsInterface(authorization, type(IServiceAuthorization).interfaceId)) { + revert IAccessAdmin.ErrorAccessAdminNotServiceAuthorization(authorization); + } + } else { + if (!ContractLib.supportsInterface(authorization, type(IAuthorization).interfaceId)) { + revert IAccessAdmin.ErrorAccessAdminNotAuthorization(authorization); + } } // check domain matches diff --git a/contracts/authorization/IAccessAdmin.sol b/contracts/authorization/IAccessAdmin.sol index 33c03b65d..6ce43f465 100644 --- a/contracts/authorization/IAccessAdmin.sol +++ b/contracts/authorization/IAccessAdmin.sol @@ -58,6 +58,7 @@ interface IAccessAdmin is // check authorization error ErrorAccessAdminAlreadyInitialized(address authorization); error ErrorAccessAdminNotAuthorization(address authorization); + error ErrorAccessAdminNotServiceAuthorization(address serviceAuthorization); error ErrorAccessAdminDomainMismatch(address authorization, ObjectType expectedDomain, ObjectType actualDomain); error ErrorAccessAdminReleaseMismatch(address authorization, VersionPart expectedRelease, VersionPart actualRelease); diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index ed00c1200..069c7bea2 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -10,13 +10,10 @@ import {IInstance} from "./IInstance.sol"; import {AccessAdmin} from "../authorization/AccessAdmin.sol"; import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; -import {NftId} from "../type/NftId.sol"; -import {ObjectType, INSTANCE, ORACLE} from "../type/ObjectType.sol"; +import {ObjectType, INSTANCE} from "../type/ObjectType.sol"; import {RoleId, RoleIdLib, ADMIN_ROLE} from "../type/RoleId.sol"; -import {Str, StrLib} from "../type/String.sol"; -import {TokenHandler} from "../shared/TokenHandler.sol"; -import {TimestampLib} from "../type/Timestamp.sol"; -import {VersionPart, VersionPartLib} from "../type/Version.sol"; +import {Str} from "../type/String.sol"; +import {VersionPart} from "../type/Version.sol"; contract InstanceAdmin is @@ -72,9 +69,9 @@ contract InstanceAdmin is /// Important: The instance MUST be registered and all instance supporting contracts must be wired to this instance. function completeSetup( address registry, - address instance, address authorization, - VersionPart release + VersionPart release, + address instance ) external reinitializer(uint64(release.toInt())) @@ -88,7 +85,7 @@ contract InstanceAdmin is registry, release); - _checkAuthorization(authorization, INSTANCE(), release, true); + _checkAuthorization(authorization, INSTANCE(), release, false, true); // effects _registry = IRegistry(registry); @@ -102,14 +99,14 @@ contract InstanceAdmin is // link nft ownability to instance _linkToNftOwnable(instance); - _setupServiceRoles(_authorization); - + // setup instance targets _createInstanceTargets(_authorization.getMainTargetName()); - // add instance authorization + // setup non-contract roles + _setupServiceRoles(_authorization); _createRoles(_authorization); - // _setupInstanceHelperTargetsWithRoles(); + // authorize functions of instance contracts _createTargetAuthorizations(_authorization); } @@ -171,7 +168,7 @@ contract InstanceAdmin is IInstanceLinkedComponent component = IInstanceLinkedComponent(componentAddress); IAuthorization authorization = component.getAuthorization(); - _checkAuthorization(address(authorization), expectedType, getRelease(), false); + _checkAuthorization(address(authorization), expectedType, getRelease(), false, false); // effects _createRoles(authorization); @@ -310,7 +307,6 @@ contract InstanceAdmin is // ------------------- Internal functions ------------------- // - function _createRoles(IAuthorization authorization) internal { diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index a032311ae..bfb578671 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -169,9 +169,9 @@ contract InstanceService is IAuthorization instanceAuthorization = InstanceAdmin(_masterInstanceAdmin).getInstanceAuthorization(); instanceAdmin.completeSetup( address(getRegistry()), - address(instance), address(instanceAuthorization), - getRelease()); + getRelease(), + address(instance)); // hard checks for newly cloned instance assert(address(instance.getRegistry()) == address(getRegistry())); diff --git a/contracts/registry/RegistryAdmin.sol b/contracts/registry/RegistryAdmin.sol index 178a01915..b19c7b85b 100644 --- a/contracts/registry/RegistryAdmin.sol +++ b/contracts/registry/RegistryAdmin.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import {IAuthorization} from "../authorization/IAuthorization.sol"; -import {IComponent} from "../shared/IComponent.sol"; import {IRegistry} from "./IRegistry.sol"; import {IService} from "../shared/IService.sol"; import {IStaking} from "../staking/IStaking.sol"; @@ -10,16 +9,10 @@ import {IStaking} from "../staking/IStaking.sol"; import {AccessAdmin} from "../authorization/AccessAdmin.sol"; import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; -import {ContractLib} from "../shared/ContractLib.sol"; -import {ObjectType, REGISTRY, STAKING, POOL, RELEASE} from "../type/ObjectType.sol"; -import {ReleaseRegistry} from "./ReleaseRegistry.sol"; -import {RoleId, RoleIdLib, ADMIN_ROLE, GIF_MANAGER_ROLE, GIF_ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; -import {Staking} from "../staking/Staking.sol"; -import {Str, StrLib} from "../type/String.sol"; -import {StakingStore} from "../staking/StakingStore.sol"; -import {TokenHandler} from "../shared/TokenHandler.sol"; -import {TokenRegistry} from "./TokenRegistry.sol"; -import {VersionPart, VersionPartLib} from "../type/Version.sol"; +import {ObjectType, REGISTRY} from "../type/ObjectType.sol"; +import {RoleId, RoleIdLib, GIF_MANAGER_ROLE, GIF_ADMIN_ROLE} from "../type/RoleId.sol"; +import {Str} from "../type/String.sol"; +import {VersionPart} from "../type/Version.sol"; /* 1) GIF_MANAGER_ROLE @@ -74,6 +67,7 @@ contract RegistryAdmin is function completeSetup( address registry, address authorization, + VersionPart release, address gifAdmin, address gifManager ) @@ -85,13 +79,12 @@ contract RegistryAdmin is // checks AccessAdminLib.checkRegistry(registry); - VersionPart release = VersionPartLib.toVersionPart(3); AccessManagerCloneable( authority()).completeSetup( registry, release); - _checkAuthorization(authorization, REGISTRY(), release, true); + _checkAuthorization(authorization, REGISTRY(), release, false, true); _registry = registry; _authorization = IAuthorization(authorization); @@ -106,14 +99,15 @@ contract RegistryAdmin is // link nft ownability to registry _linkToNftOwnable(_registry); - // register core targets + // setup registry core targets _createCoreTargets(_authorization.getMainTargetName()); - // setup authorization for registry and supporting contracts + // setup non-contract roles _createRoles(_authorization); _grantRoleToAccount(GIF_ADMIN_ROLE(), gifAdmin); _grantRoleToAccount(GIF_MANAGER_ROLE(), gifManager); + // authorize functions of registry and staking contracts _createTargetAuthorizations(_authorization); } diff --git a/contracts/registry/ReleaseAdmin.sol b/contracts/registry/ReleaseAdmin.sol index 9e139abf3..f3930fc02 100644 --- a/contracts/registry/ReleaseAdmin.sol +++ b/contracts/registry/ReleaseAdmin.sol @@ -9,7 +9,7 @@ import {IServiceAuthorization} from "../authorization/IServiceAuthorization.sol" import {AccessAdmin} from "../authorization/AccessAdmin.sol"; import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; -import {ObjectType, ObjectTypeLib} from "../type/ObjectType.sol"; +import {ObjectType, ObjectTypeLib, RELEASE} from "../type/ObjectType.sol"; import {RoleId, ADMIN_ROLE, RELEASE_REGISTRY_ROLE} from "../type/RoleId.sol"; import {Str} from "../type/String.sol"; import {VersionPart} from "../type/Version.sol"; @@ -34,6 +34,8 @@ contract ReleaseAdmin is /// @dev release core targets string public constant RELEASE_ADMIN_TARGET_NAME = "ReleaseAdmin"; + IServiceAuthorization internal _serviceAuthorization; + modifier onlyReleaseRegistry() { (bool isMember, ) = _authority.hasRole(RELEASE_REGISTRY_ROLE().toInt(), msg.sender); @@ -54,8 +56,9 @@ contract ReleaseAdmin is function completeSetup( address registry, - address releaseRegistry, - VersionPart release + address authorization, + VersionPart release, + address releaseRegistry ) external reinitializer(uint64(release.toInt())) @@ -67,12 +70,19 @@ contract ReleaseAdmin is AccessManagerCloneable( authority()).completeSetup( registry, - release); + release); + + IServiceAuthorization serviceAuthorization = IServiceAuthorization(authorization); + _checkAuthorization(address(serviceAuthorization), RELEASE(), release, true, true); + _serviceAuthorization = serviceAuthorization; // link nft ownability to registry _linkToNftOwnable(registry); + // setup release contract _setupReleaseRegistry(releaseRegistry); + + // relase services will be authorized one by one via authorizeService() } @@ -80,7 +90,6 @@ contract ReleaseAdmin is /// For all authorized services its authorized functions are enabled. /// Permissioned function: Access is restricted to release registry. function authorizeService( - IServiceAuthorization serviceAuthorization, IService service, ObjectType serviceDomain, VersionPart release @@ -91,12 +100,12 @@ contract ReleaseAdmin is _createServiceTargetAndRole(service, serviceDomain, release); // authorize functions of service - Str serviceTarget = serviceAuthorization.getServiceTarget(serviceDomain); - RoleId[] memory authorizedRoles = serviceAuthorization.getAuthorizedRoles(serviceTarget); + Str serviceTarget = _serviceAuthorization.getServiceTarget(serviceDomain); + RoleId[] memory authorizedRoles = _serviceAuthorization.getAuthorizedRoles(serviceTarget); for (uint256 i = 0; i < authorizedRoles.length; i++) { _authorizeFunctions( - IAuthorization(address(serviceAuthorization)), + IAuthorization(address(_serviceAuthorization)), serviceTarget, authorizedRoles[i]); } diff --git a/contracts/registry/ReleaseRegistry.sol b/contracts/registry/ReleaseRegistry.sol index 5fb525946..6cd5aad0d 100644 --- a/contracts/registry/ReleaseRegistry.sol +++ b/contracts/registry/ReleaseRegistry.sol @@ -150,7 +150,7 @@ contract ReleaseRegistry is uint256 serviceDomainsCount = _verifyServiceAuthorization(serviceAuthorization, releaseVersion, salt); // create and initialize release admin - releaseAdmin = _cloneNewReleaseAdmin(releaseVersion); + releaseAdmin = _cloneNewReleaseAdmin(serviceAuthorization, releaseVersion); releaseSalt = salt; // ensures unique salt @@ -216,7 +216,6 @@ contract ReleaseRegistry is ReleaseAdmin releaseAdmin = ReleaseAdmin(_releaseInfo[releaseVersion].releaseAdmin); releaseAdmin.setReleaseLocked(false); releaseAdmin.authorizeService( - releaseAuthz, service, serviceDomain, releaseVersion); @@ -367,7 +366,10 @@ contract ReleaseRegistry is _releaseInfo[release].releaseAdmin).setReleaseLocked(locked); } - function _cloneNewReleaseAdmin(VersionPart release) + function _cloneNewReleaseAdmin( + IServiceAuthorization serviceAuthorization, + VersionPart release + ) private returns (ReleaseAdmin clonedAdmin) { @@ -377,7 +379,7 @@ contract ReleaseRegistry is string memory releaseAdminName = string( abi.encodePacked( - "ReleaseAdmin_v", + "ReleaseAdminV", release.toString())); clonedAdmin.initialize( @@ -386,8 +388,9 @@ contract ReleaseRegistry is clonedAdmin.completeSetup( address(_registry), - address(this), // release registry (this contract) - release); + address(serviceAuthorization), + release, + address(this)); // release registry (this contract) // lock release (remains locked until activation) clonedAdmin.setReleaseLocked(true); diff --git a/contracts/registry/TokenRegistry.sol b/contracts/registry/TokenRegistry.sol index d20ad3fbb..302084d6a 100644 --- a/contracts/registry/TokenRegistry.sol +++ b/contracts/registry/TokenRegistry.sol @@ -3,15 +3,13 @@ pragma solidity ^0.8.20; import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; - -import {REGISTRY} from "../type/ObjectType.sol"; -import {VersionPart} from "../type/Version.sol"; import {IRegistry} from "./IRegistry.sol"; import {IRegistryLinked} from "../shared/IRegistryLinked.sol"; + import {ReleaseRegistry} from "./ReleaseRegistry.sol"; import {RegistryAdmin} from "./RegistryAdmin.sol"; +import {VersionPart} from "../type/Version.sol"; /// @dev The TokenRegistry contract is used to whitelist/manage ERC-20 of tokens per major release. /// Only whitelisted tokens can be used as default tokens for products, distribution and pools components. diff --git a/test/authorization/RegistryAdminEx.sol b/test/authorization/RegistryAdminEx.sol index 66be31a00..78c18e58f 100644 --- a/test/authorization/RegistryAdminEx.sol +++ b/test/authorization/RegistryAdminEx.sol @@ -2,14 +2,12 @@ pragma solidity ^0.8.20; import {IAccess} from "../../contracts/authorization/IAccess.sol"; -import {IAuthorization} from "../../contracts/authorization/IAuthorization.sol"; -import {IRegistry} from "../../contracts/registry/IRegistry.sol"; import {AccessAdminLib} from "../../contracts/authorization/AccessAdminLib.sol"; import {AccessManagedMock} from "../mock/AccessManagedMock.sol"; import {PUBLIC_ROLE} from "../../contracts/type/RoleId.sol"; import {RegistryAdmin} from "../../contracts/registry/RegistryAdmin.sol"; -import {RegistryAuthorization} from "../../contracts/registry/RegistryAuthorization.sol"; +import {VersionPart} from "../../contracts/type/Version.sol"; contract RegistryAdminEx is RegistryAdmin { @@ -18,6 +16,7 @@ contract RegistryAdminEx is RegistryAdmin { function completeSetup( address registry, address authorization, + VersionPart release, address gifAdmin, address gifManager ) @@ -27,6 +26,7 @@ contract RegistryAdminEx is RegistryAdmin { super.completeSetup( registry, authorization, + release, gifAdmin, gifManager); diff --git a/test/base/GifDeployer.sol b/test/base/GifDeployer.sol index 4dd4f559e..c165a8f81 100644 --- a/test/base/GifDeployer.sol +++ b/test/base/GifDeployer.sol @@ -69,6 +69,7 @@ import {StakingServiceManager} from "../../contracts/staking/StakingServiceManag contract GifDeployer is Test { + uint8 public constant GIF_RELEASE = 3; string public constant COMMIT_HASH = "1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a"; struct DeployedServiceInfo { @@ -176,30 +177,93 @@ contract GifDeployer is Test { Staking staking ) { + // solhint-disable vm.startPrank(gifManager); - // 1) deploy dip token + console.log("1) deploy dip token"); dip = new Dip(); - // 2) deploy registry admin + console.log("2) deploy registry contracts"); + ( + registry, + tokenRegistry, + releaseRegistry, + registryAdmin + ) = _deployRegistry(dip); + + console.log("3) deploy staking contracts"); + ( + stakingManager, + staking + ) = _deployStaking(registry, tokenRegistry); + + console.log("4) complete setup for GIF core contracts"); + + console.log(" a) initialize registry"); + registry.initialize( + address(releaseRegistry), + address(tokenRegistry), + address(staking)); + + console.log(" b) link staking to its registered nft id"); + staking.linkToRegisteredNftId(); + + console.log(" c) complete registry admin setup"); + registryAdmin.completeSetup( + address(registry), + address(new RegistryAuthorization(COMMIT_HASH)), + VersionPartLib.toVersionPart(GIF_RELEASE), + gifAdmin, + gifManager); + + console.log("GIF core contracts deployd and setup completed"); + + vm.stopPrank(); + // solhint-disable enable + } + + + function _deployRegistry(IERC20Metadata dip) + internal + returns ( + Registry registry, + TokenRegistry tokenRegistry, + ReleaseRegistry releaseRegistry, + RegistryAdmin registryAdmin + ) + { + + console.log(" a) deploy registry admin"); registryAdmin = new RegistryAdmin(); - // 3) deploy registry + console.log(" b) deploy registry"); registry = new Registry(registryAdmin, globalRegistry); - // 4) deploy release manager + console.log(" c) deploy release registry"); releaseRegistry = new ReleaseRegistry(registry); - // 5) deploy token registry + console.log(" d) deploy token registry"); tokenRegistry = new TokenRegistry(registry, dip); + } + - // 6) deploy staking reader + function _deployStaking( + Registry registry, + TokenRegistry tokenRegistry + ) + internal + returns ( + StakingManager stakingManager, + Staking staking + ) + { + console.log(" a) deploy staking reader"); StakingReader stakingReader = new StakingReader(registry); - // 7) deploy staking store + console.log(" b) deploy staking store"); StakingStore stakingStore = new StakingStore(registry, stakingReader); - // 8) deploy staking manager and staking component + console.log(" c) deploy staking manager (including upgradeable staking)"); stakingManager = new StakingManager( address(registry), address(tokenRegistry), @@ -208,27 +272,10 @@ contract GifDeployer is Test { bytes32("")); // salt staking = stakingManager.getStaking(); - // 9) initialize instance reader + console.log(" d) initialize staking reader"); stakingReader.initialize( address(staking), address(stakingStore)); - - // 10) intialize registry and register staking component - registry.initialize( - address(releaseRegistry), - address(tokenRegistry), - address(staking)); - - staking.linkToRegisteredNftId(); - - // 11) initialize registry admin - registryAdmin.completeSetup( - address(registry), - address(new RegistryAuthorization(COMMIT_HASH)), - gifAdmin, - gifManager); - - vm.stopPrank(); } diff --git a/test/base/GifTest.sol b/test/base/GifTest.sol index 74dc2e6c9..5af0739a3 100644 --- a/test/base/GifTest.sol +++ b/test/base/GifTest.sol @@ -268,16 +268,13 @@ contract GifTest is GifDeployer { // instance service is now ready to create cloned instances masterInstanceNftId = instanceService.setAndRegisterMasterInstance(address(masterInstance)); -console.log("g"); // setup roles, targets and function grantings instanceAuthorizationV3 = new InstanceAuthorizationV3(); -console.log("h"); masterInstanceAdmin.completeSetup( address(registry), - address(masterInstance), address(instanceAuthorizationV3), - VersionPartLib.toVersionPart(3)); -console.log("i"); + VersionPartLib.toVersionPart(3), + address(masterInstance)); require(address(masterInstanceAdmin.getRegistry()) == address(registry), "unexpected master instance registry"); require(masterInstanceAdmin.getRelease().toInt() == 3, "unexpected master instance release"); @@ -285,7 +282,6 @@ console.log("i"); // MUST be set after instance is set up and registered // lock master instance nft chainNft.transferFrom(registryOwner, NFT_LOCK_ADDRESS, masterInstanceNftId.toInt()); -console.log("j"); // solhint-disable console.log("master instance deployed at", address(masterInstance)); From e2e958f1e2c90e57a8e14530ae74b5b3f5b935f0 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Sun, 1 Sep 2024 15:28:30 +0000 Subject: [PATCH 12/18] fix hh deploy script --- scripts/libs/instance.ts | 2 +- scripts/libs/registry.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/libs/instance.ts b/scripts/libs/instance.ts index 4219751ab..a26f6a8b2 100644 --- a/scripts/libs/instance.ts +++ b/scripts/libs/instance.ts @@ -212,9 +212,9 @@ export async function deployAndRegisterMasterInstance( await executeTx( () => masterInstanceAdmin.completeSetup( registry.registryAddress, - masterInstanceAddress, masterInstanceAuthorizationV3Address, 3, + masterInstanceAddress, getTxOpts()), "masterInstanceAdmin completeSetup", [masterInstanceAdmin.interface] diff --git a/scripts/libs/registry.ts b/scripts/libs/registry.ts index 7804ddb81..31ecbd78d 100644 --- a/scripts/libs/registry.ts +++ b/scripts/libs/registry.ts @@ -275,7 +275,13 @@ export async function deployAndInitializeRegistry(owner: Signer, libraries: Libr ); await executeTx( - async () => await registryAdmin.completeSetup(registry, registryAuthorization, owner, owner, getTxOpts()), + async () => await registryAdmin.completeSetup( + registry, + registryAuthorization, + 3, + owner, + owner, + getTxOpts()), "registryAdmin.completeSetup", [registryAdmin.interface] ); From a06f24ddb7ba02baaecc929c24b7bdccb61f0daa Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Sun, 1 Sep 2024 16:14:36 +0000 Subject: [PATCH 13/18] complete custom target locking test cases --- contracts/authorization/AccessAdmin.sol | 2 +- test/instance/Instance.t.sol | 16 +++++++--- .../authorization/InstanceAuthzTargets.t.sol | 32 +++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index 26ecb088c..942c43f52 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -303,7 +303,7 @@ contract AccessAdmin is } function isTargetLocked(address target) public view returns (bool locked) { - return _authority.isTargetClosed(target); + return _authority.isLocked() || _authority.isTargetClosed(target); } function authorizedFunctions(address target) external view returns (uint256 numberOfFunctions) { diff --git a/test/instance/Instance.t.sol b/test/instance/Instance.t.sol index 67496d7c7..5768088d0 100644 --- a/test/instance/Instance.t.sol +++ b/test/instance/Instance.t.sol @@ -68,8 +68,8 @@ contract TestInstance is GifTest { // THEN assertTrue(instance.isInstanceLocked(), "instance is not locked"); - assertFalse(instance.isTargetLocked(address(pool)), "pool is locked"); - assertFalse(instance.isTargetLocked(address(distribution)), "distribution is locked"); + assertTrue(instance.isTargetLocked(address(pool)), "pool is not locked"); + assertTrue(instance.isTargetLocked(address(distribution)), "distribution is not locked"); // WHEN + THEN vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, address(poolOwner))); @@ -108,7 +108,7 @@ contract TestInstance is GifTest { instance.setInstanceLocked(true); assertTrue(instance.isInstanceLocked(), "instance is not locked (before)"); - assertFalse(instance.isTargetLocked(address(pool)), "pool is locked (before)"); + assertTrue(instance.isTargetLocked(address(pool)), "pool is not locked (before)"); // WHEN lock pool while instance is locked vm.prank(instanceOwner); @@ -124,7 +124,15 @@ contract TestInstance is GifTest { // THEN assertTrue(instance.isInstanceLocked(), "instance is not locked (after 2)"); - assertFalse(instance.isTargetLocked(address(pool)), "pool is locked (after 2)"); + assertTrue(instance.isTargetLocked(address(pool)), "pool is not locked (after 2)"); + + // WHEN unlock instance + vm.prank(instanceOwner); + instance.setInstanceLocked(false); + + // THEN + assertFalse(instance.isInstanceLocked(), "instance is not locked (after 3)"); + assertFalse(instance.isTargetLocked(address(pool)), "pool is not locked (after 3)"); } diff --git a/test/instance/authorization/InstanceAuthzTargets.t.sol b/test/instance/authorization/InstanceAuthzTargets.t.sol index 712b7c79b..32b34a782 100644 --- a/test/instance/authorization/InstanceAuthzTargets.t.sol +++ b/test/instance/authorization/InstanceAuthzTargets.t.sol @@ -53,6 +53,7 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { // check target info assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); + assertFalse(instanceReader.isLocked(address(target)), "target locked"); IAccess.TargetInfo memory targetInfo = instanceReader.getTargetInfo(address(target)); assertEq(targetInfo.name.toString(), "MyTarget", "unexpected target name"); assertTrue(targetInfo.targetType == IAccess.TargetType.Custom, "target type not custom"); @@ -74,7 +75,38 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { } + // check locking of custom targets works the same as for targets created via authorization spec function test_instanceAuthzTargetsSetTargetLockedHappyCase() public { + // GIVEN + AccessManagedMock target = _deployAccessManagedMock(); + string memory targetName = "MyTarget"; + + vm.prank(instanceOwner); + RoleId myTargetRoleId = instance.createTarget(address(target), targetName); + + assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); + assertFalse(instanceReader.isLocked(address(target)), "target locked"); + + // WHEN - lock target + vm.prank(instanceOwner); + instance.setTargetLocked(address(target), true); + + // THEN + assertTrue(instanceReader.isLocked(address(target)), "target not locked after set locked"); + + // WHEN - unlock target + vm.prank(instanceOwner); + instance.setTargetLocked(address(target), false); + + // THEN + assertFalse(instanceReader.isLocked(address(target)), "target locked (2)"); + + // WHEN - lock target via instance lock + vm.prank(instanceOwner); + instance.setInstanceLocked(true); + + // THEN + assertTrue(instanceReader.isLocked(address(target)), "target not locked after locking instance"); } //--- helper functions ----------------------------------------------------// From b4e09b3a96584e8e69eecb46f2147c341bb413ba Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Mon, 2 Sep 2024 20:11:28 +0000 Subject: [PATCH 14/18] add IInstance.authorizeFunctions --- contracts/authorization/AccessAdmin.sol | 2 +- contracts/instance/IInstance.sol | 7 ++- contracts/instance/IInstanceService.sol | 7 ++- contracts/instance/Instance.sol | 14 +++--- contracts/instance/InstanceAdmin.sol | 41 +++++++++++++---- .../instance/InstanceAuthorizationV3.sol | 29 ++++++------ contracts/instance/InstanceService.sol | 18 +++++++- contracts/registry/ServiceAuthorizationV3.sol | 1 + .../authorization/InstanceAuthzTargets.t.sol | 44 +++++++++++++++++++ 9 files changed, 129 insertions(+), 34 deletions(-) diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index 942c43f52..4ac97a207 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -294,7 +294,7 @@ contract AccessAdmin is return _targets[idx]; } - function getTargetInfo(address target) external view returns (TargetInfo memory targetInfo) { + function getTargetInfo(address target) public view returns (TargetInfo memory targetInfo) { return _targetInfo[target]; } diff --git a/contracts/instance/IInstance.sol b/contracts/instance/IInstance.sol index 08d65515b..27ffdd899 100644 --- a/contracts/instance/IInstance.sol +++ b/contracts/instance/IInstance.sol @@ -1,13 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IAccess} from "../authorization/IAccess.sol"; +import {IRegisterable} from "../shared/IRegisterable.sol"; + import {Amount} from "../type/Amount.sol"; import {BundleSet} from "./BundleSet.sol"; import {RiskSet} from "./RiskSet.sol"; import {InstanceAdmin} from "./InstanceAdmin.sol"; import {InstanceReader} from "./InstanceReader.sol"; import {InstanceStore} from "./InstanceStore.sol"; -import {IRegisterable} from "../shared/IRegisterable.sol"; import {NftId} from "../type/NftId.sol"; import {RoleId} from "../type/RoleId.sol"; import {Seconds} from "../type/Seconds.sol"; @@ -115,7 +117,8 @@ interface IInstance is /// Custom targets are not intended to be used for components. function createTarget(address target, string memory name) external returns (RoleId contractRoleId); - function setTargetFunctionRole(string memory targetName, bytes4[] calldata selectors, RoleId roleId) external; + /// @dev Authorizes the specified functions for the target and provided role. + function authorizeFunctions(address target, RoleId roleId, IAccess.FunctionInfo[] memory functions) external; //--- getters -----------------------------------------------------------// diff --git a/contracts/instance/IInstanceService.sol b/contracts/instance/IInstanceService.sol index 1f77e0e4b..b71b15049 100644 --- a/contracts/instance/IInstanceService.sol +++ b/contracts/instance/IInstanceService.sol @@ -1,9 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {Amount} from "../type/Amount.sol"; +import {IAccess} from "../authorization/IAccess.sol"; import {IInstance} from "./IInstance.sol"; import {IService} from "../shared/IService.sol"; + +import {Amount} from "../type/Amount.sol"; import {NftId} from "../type/NftId.sol"; import {ObjectType} from "../type/ObjectType.sol"; import {RoleId} from "../type/RoleId.sol"; @@ -70,6 +72,9 @@ interface IInstanceService is IService { /// All custom trargets are created with a corresponding contract role. function createTarget(address target, string memory name) external returns (RoleId contractRoleId); + /// @dev Authorizes the specified functions for the specified target. + function authorizeFunctions(address target, RoleId roleId, IAccess.FunctionInfo[] memory functions) external; + /// @dev Locks/unlocks the specified target constrolled by the corresponding instance admin. function setTargetLocked(address target, bool locked) external; diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index 0a3851778..c7e2e4fe3 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IAccess} from "../authorization/IAccess.sol"; import {IInstance} from "./IInstance.sol"; import {IComponentService} from "../shared/IComponentService.sol"; import {IInstanceService} from "./IInstanceService.sol"; @@ -260,17 +261,16 @@ contract Instance is } - function setTargetFunctionRole( - string memory targetName, - bytes4[] calldata selectors, - RoleId roleId - ) + function authorizeFunctions( + address target, + RoleId roleId, + IAccess.FunctionInfo[] memory functions + ) external restricted() onlyOwner() { - // TODO refactor - // _instanceAdmin.setTargetFunctionRoleByInstance(targetName, selectors, roleId); + _instanceService.authorizeFunctions(target, roleId, functions); } //--- initial setup functions -------------------------------------------// diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index 069c7bea2..46f3387f4 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -25,19 +25,23 @@ contract InstanceAdmin is string public constant BUNDLE_SET_TARGET_NAME = "BundleSet"; string public constant RISK_SET_TARGET_NAME = "RiskSet"; + // onlyInstanceService error ErrorInstanceAdminNotInstanceService(address caller); - error ErrorInstanceAdminNotCustomRole(RoleId roleId); + // error ErrorInstanceAdminNotCustomRole(RoleId roleId); - error ErrorInstanceAdminNotRegistered(address instance); - error ErrorInstanceAdminAlreadyAuthorized(address instance); + // error ErrorInstanceAdminNotRegistered(address instance); + // error ErrorInstanceAdminAlreadyAuthorized(address instance); - error ErrorInstanceAdminNotComponentRole(RoleId roleId); - error ErrorInstanceAdminRoleAlreadyExists(RoleId roleId); - error ErrorInstanceAdminRoleTypeNotContract(RoleId roleId, IAccess.RoleType roleType); + // error ErrorInstanceAdminNotComponentRole(RoleId roleId); + // error ErrorInstanceAdminRoleAlreadyExists(RoleId roleId); + // error ErrorInstanceAdminRoleTypeNotContract(RoleId roleId, IAccess.RoleType roleType); - error ErrorInstanceAdminReleaseMismatch(); - error ErrorInstanceAdminExpectedTargetMissing(string targetName); + // error ErrorInstanceAdminReleaseMismatch(); + // error ErrorInstanceAdminExpectedTargetMissing(string targetName); + + // authorizeFunctions + error ErrorInstanceAdminNotComponentOrCustomTarget(address target); IInstance internal _instance; IRegistry internal _registry; @@ -261,6 +265,27 @@ contract InstanceAdmin is } + /// @dev Add function authorizations for the specified component or custom target. + function authorizeFunctions( + address target, + RoleId roleId, + IAccess.FunctionInfo[] memory functions + ) + external + restricted() + onlyExistingTarget(target) + { + // check target type + IAccess.TargetType targetType = getTargetInfo(target).targetType; + if (targetType != TargetType.Component && targetType != TargetType.Custom) { + revert ErrorInstanceAdminNotComponentOrCustomTarget(target); + } + + _authorizeTargetFunctions(target, roleId, functions, true); + } + + + /// @dev locks the instance and all its releated targets including component and custom targets. function setInstanceLocked(bool locked) external // not restricted(): need to operate on locked instances to unlock instance diff --git a/contracts/instance/InstanceAuthorizationV3.sol b/contracts/instance/InstanceAuthorizationV3.sol index e0a974cbe..21b4fc186 100644 --- a/contracts/instance/InstanceAuthorizationV3.sol +++ b/contracts/instance/InstanceAuthorizationV3.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.20; import {IAccess} from "../authorization/IAccess.sol"; +import {IInstance} from "../instance/Instance.sol"; import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {Authorization} from "../authorization/Authorization.sol"; import {ACCOUNTING, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, APPLICATION, POLICY, CLAIM, BUNDLE, RISK} from "../../contracts/type/ObjectType.sol"; import {BundleSet} from "../instance/BundleSet.sol"; -import {Instance} from "../instance/Instance.sol"; import {InstanceAdmin} from "../instance/InstanceAdmin.sol"; import {InstanceStore} from "../instance/InstanceStore.sol"; import {ADMIN_ROLE, INSTANCE_OWNER_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; @@ -132,26 +132,26 @@ contract InstanceAuthorizationV3 // authorize instance service role functions = _authorizeForTarget(INSTANCE_TARGET_NAME, PUBLIC_ROLE()); - _authorize(functions, Instance.registerProduct.selector, "registerProduct"); - _authorize(functions, Instance.upgradeInstanceReader.selector, "upgradeInstanceReader"); + _authorize(functions, IInstance.registerProduct.selector, "registerProduct"); + _authorize(functions, IInstance.upgradeInstanceReader.selector, "upgradeInstanceReader"); // staking - _authorize(functions, Instance.setStakingLockingPeriod.selector, "setStakingLockingPeriod"); - _authorize(functions, Instance.setStakingRewardRate.selector, "setStakingRewardRate"); - _authorize(functions, Instance.refillStakingRewardReserves.selector, "refillStakingRewardReserves"); - _authorize(functions, Instance.withdrawStakingRewardReserves.selector, "withdrawStakingRewardReserves"); + _authorize(functions, IInstance.setStakingLockingPeriod.selector, "setStakingLockingPeriod"); + _authorize(functions, IInstance.setStakingRewardRate.selector, "setStakingRewardRate"); + _authorize(functions, IInstance.refillStakingRewardReserves.selector, "refillStakingRewardReserves"); + _authorize(functions, IInstance.withdrawStakingRewardReserves.selector, "withdrawStakingRewardReserves"); // custom authz - _authorize(functions, Instance.createRole.selector, "createRole"); - _authorize(functions, Instance.setRoleActive.selector, "setRoleActive"); - _authorize(functions, Instance.grantRole.selector, "grantRole"); - _authorize(functions, Instance.revokeRole.selector, "revokeRole"); - _authorize(functions, Instance.createTarget.selector, "createTarget"); - _authorize(functions, Instance.setTargetFunctionRole.selector, "setTargetFunctionRole"); + _authorize(functions, IInstance.createRole.selector, "createRole"); + _authorize(functions, IInstance.setRoleActive.selector, "setRoleActive"); + _authorize(functions, IInstance.grantRole.selector, "grantRole"); + _authorize(functions, IInstance.revokeRole.selector, "revokeRole"); + _authorize(functions, IInstance.createTarget.selector, "createTarget"); + _authorize(functions, IInstance.authorizeFunctions.selector, "authorizeFunctions"); // authorize instance service role functions = _authorizeForTarget(INSTANCE_TARGET_NAME, getServiceRole(INSTANCE())); - _authorize(functions, Instance.setInstanceReader.selector, "setInstanceReader"); + _authorize(functions, IInstance.setInstanceReader.selector, "setInstanceReader"); } @@ -168,6 +168,7 @@ contract InstanceAuthorizationV3 _authorize(functions, InstanceAdmin.revokeRole.selector, "revokeRole"); _authorize(functions, InstanceAdmin.createTarget.selector, "createTarget"); + _authorize(functions, InstanceAdmin.authorizeFunctions.selector, "authorizeFunctions"); _authorize(functions, InstanceAdmin.setTargetLocked.selector, "setTargetLocked"); _authorize(functions, InstanceAdmin.setInstanceLocked.selector, "setInstanceLocked"); diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index bfb578671..60b9ff467 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; +import {IAccess} from "../authorization/IAccess.sol"; import {IAuthorization} from "../authorization/IAuthorization.sol"; import {IComponentService} from "../shared/IComponentService.sol"; import {IInstance} from "./IInstance.sol"; @@ -120,7 +121,22 @@ contract InstanceService is } - /// @dev Locks/unlocks the specified target constrolled by the corresponding instance admin. + /// @inheritdoc IInstanceService + function authorizeFunctions( + address target, + RoleId roleId, + IAccess.FunctionInfo[] memory functions + ) + external + restricted() + onlyInstance() + { + IInstance instance = IInstance(msg.sender); + return instance.getInstanceAdmin().authorizeFunctions(target, roleId, functions); + } + + + /// @inheritdoc IInstanceService function setTargetLocked(address target, bool locked) external virtual diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index b5fc8a5d1..cc545b844 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -141,6 +141,7 @@ contract ServiceAuthorizationV3 _authorize(functions, IInstanceService.revokeRole.selector, "revokeRole"); _authorize(functions, IInstanceService.createTarget.selector, "createTarget"); + _authorize(functions, IInstanceService.authorizeFunctions.selector, "authorizeFunctions"); _authorize(functions, IInstanceService.setTargetLocked.selector, "setTargetLocked"); _authorize(functions, IInstanceService.setInstanceLocked.selector, "setInstanceLocked"); diff --git a/test/instance/authorization/InstanceAuthzTargets.t.sol b/test/instance/authorization/InstanceAuthzTargets.t.sol index 32b34a782..242743625 100644 --- a/test/instance/authorization/InstanceAuthzTargets.t.sol +++ b/test/instance/authorization/InstanceAuthzTargets.t.sol @@ -1,12 +1,14 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; import {console} from "../../../lib/forge-std/src/Test.sol"; import {IAccess} from "../../../contracts/authorization/IAccess.sol"; import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; import {IInstance} from "../../../contracts/instance/IInstance.sol"; +import {AccessAdminLib} from "../../../contracts/authorization/AccessAdminLib.sol"; import {AccessManagedMock} from "../../mock/AccessManagedMock.sol"; import {InstanceAuthzBaseTest} from "./InstanceAuthzBase.t.sol"; import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; @@ -109,6 +111,48 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { assertTrue(instanceReader.isLocked(address(target)), "target not locked after locking instance"); } + function test_instanceAuthzTargetsAuthorizeFunctionsHappyCase() public { + // GIVEN + AccessManagedMock target = _deployAccessManagedMock(); + string memory targetName = "MyTarget"; + + vm.prank(instanceOwner); + instance.createTarget(address(target), targetName); + + assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); + assertFalse(instanceReader.isLocked(address(target)), "target locked"); + assertEq(target.counter1(), 0, "unexpected initial counter value"); + + // WHEN + THEN attempt to call unauthorized function + vm.expectRevert( + abi.encodeWithSelector( + IAccessManaged.AccessManagedUnauthorized.selector, + outsider)); + + vm.prank(outsider); + target.increaseCounter1(); + + assertEq(target.counter1(), 0, "unexpected initial counter value"); + + // WHEN - add function authz + RoleId publicRoleId = instance.getInstanceAdmin().getPublicRole(); + IAccess.FunctionInfo[] memory functions = new IAccess.FunctionInfo[](1); + functions[0] = AccessAdminLib.toFunction({ + name: "increaseCounter1", + selector: AccessManagedMock.increaseCounter1.selector}); + + vm.prank(instanceOwner); + instance.authorizeFunctions(address(target), publicRoleId, functions); + + // must not revert now + vm.prank(outsider); + target.increaseCounter1(); + + // THEN + assertEq(target.counter1(), 1, "unexpected counter value after increaseCounter1"); + } + + //--- helper functions ----------------------------------------------------// function _deployAccessManagedMock() internal returns (AccessManagedMock accessManagedMock) { From 44a4322c592f49483d9ae2d691b6181461cabeae Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Mon, 2 Sep 2024 22:31:21 +0000 Subject: [PATCH 15/18] add unauthorizeFunctions --- contracts/authorization/AccessAdminLib.sol | 9 +++ contracts/authorization/IAccessAdmin.sol | 4 ++ contracts/instance/IInstance.sol | 3 + contracts/instance/IInstanceService.sol | 3 + contracts/instance/Instance.sol | 14 ++++ contracts/instance/InstanceAdmin.sol | 62 ++++++++++-------- .../instance/InstanceAuthorizationV3.sol | 2 + contracts/instance/InstanceReader.sol | 7 +- contracts/instance/InstanceService.sol | 14 ++++ contracts/registry/ServiceAuthorizationV3.sol | 1 + .../authorization/InstanceAuthzTargets.t.sol | 65 ++++++++++++++++++- 11 files changed, 153 insertions(+), 31 deletions(-) diff --git a/contracts/authorization/AccessAdminLib.sol b/contracts/authorization/AccessAdminLib.sol index f639ffcd7..7d84687e4 100644 --- a/contracts/authorization/AccessAdminLib.sol +++ b/contracts/authorization/AccessAdminLib.sol @@ -370,6 +370,7 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB }); } + function toFunction( bytes4 selector, string memory name @@ -378,6 +379,14 @@ library AccessAdminLib { // ACCESS_ADMIN_LIB view returns (IAccess.FunctionInfo memory) { + if(selector == bytes4(0)) { + revert IAccessAdmin.ErrorAccessAdminSelectorZero(); + } + + if(bytes(name).length == 0) { + revert IAccessAdmin.ErrorAccessAdminFunctionNameEmpty(); + } + return IAccess.FunctionInfo({ name: StrLib.toStr(name), selector: SelectorLib.toSelector(selector), diff --git a/contracts/authorization/IAccessAdmin.sol b/contracts/authorization/IAccessAdmin.sol index 6ce43f465..7a032d8b2 100644 --- a/contracts/authorization/IAccessAdmin.sol +++ b/contracts/authorization/IAccessAdmin.sol @@ -96,6 +96,10 @@ interface IAccessAdmin is // authorize target functions error ErrorAccessAdminAuthorizeForAdminRoleInvalid(address target); + // toFunction + error ErrorAccessAdminSelectorZero(); + error ErrorAccessAdminFunctionNameEmpty(); + // check target error ErrorAccessAdminTargetUnknown(address target); diff --git a/contracts/instance/IInstance.sol b/contracts/instance/IInstance.sol index 27ffdd899..cfa6c5d36 100644 --- a/contracts/instance/IInstance.sol +++ b/contracts/instance/IInstance.sol @@ -120,6 +120,9 @@ interface IInstance is /// @dev Authorizes the specified functions for the target and provided role. function authorizeFunctions(address target, RoleId roleId, IAccess.FunctionInfo[] memory functions) external; + /// @dev Removes any role authorization for the specified functions. + function unauthorizeFunctions(address target, IAccess.FunctionInfo[] memory functions) external; + //--- getters -----------------------------------------------------------// /// @dev returns the overall locking state of the instance (including all components) diff --git a/contracts/instance/IInstanceService.sol b/contracts/instance/IInstanceService.sol index b71b15049..07fe85f50 100644 --- a/contracts/instance/IInstanceService.sol +++ b/contracts/instance/IInstanceService.sol @@ -75,6 +75,9 @@ interface IInstanceService is IService { /// @dev Authorizes the specified functions for the specified target. function authorizeFunctions(address target, RoleId roleId, IAccess.FunctionInfo[] memory functions) external; + /// @dev Removes any role authorization for the specified functions. + function unauthorizeFunctions(address target, IAccess.FunctionInfo[] memory functions) external; + /// @dev Locks/unlocks the specified target constrolled by the corresponding instance admin. function setTargetLocked(address target, bool locked) external; diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index c7e2e4fe3..2300607ba 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -261,6 +261,7 @@ contract Instance is } + /// @inheritdoc IInstance function authorizeFunctions( address target, RoleId roleId, @@ -273,6 +274,19 @@ contract Instance is _instanceService.authorizeFunctions(target, roleId, functions); } + + /// @inheritdoc IInstance + function unauthorizeFunctions( + address target, + IAccess.FunctionInfo[] memory functions + ) + external + restricted() + onlyOwner() + { + _instanceService.unauthorizeFunctions(target, functions); + } + //--- initial setup functions -------------------------------------------// diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index 46f3387f4..258607c73 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -28,18 +28,6 @@ contract InstanceAdmin is // onlyInstanceService error ErrorInstanceAdminNotInstanceService(address caller); - // error ErrorInstanceAdminNotCustomRole(RoleId roleId); - - // error ErrorInstanceAdminNotRegistered(address instance); - // error ErrorInstanceAdminAlreadyAuthorized(address instance); - - // error ErrorInstanceAdminNotComponentRole(RoleId roleId); - // error ErrorInstanceAdminRoleAlreadyExists(RoleId roleId); - // error ErrorInstanceAdminRoleTypeNotContract(RoleId roleId, IAccess.RoleType roleType); - - // error ErrorInstanceAdminReleaseMismatch(); - // error ErrorInstanceAdminExpectedTargetMissing(string targetName); - // authorizeFunctions error ErrorInstanceAdminNotComponentOrCustomTarget(address target); @@ -275,16 +263,25 @@ contract InstanceAdmin is restricted() onlyExistingTarget(target) { - // check target type - IAccess.TargetType targetType = getTargetInfo(target).targetType; - if (targetType != TargetType.Component && targetType != TargetType.Custom) { - revert ErrorInstanceAdminNotComponentOrCustomTarget(target); - } - + _checkComponentOrCustomTarget(target); _authorizeTargetFunctions(target, roleId, functions, true); } + /// @dev Removes function authorizations for the specified component or custom target. + function unauthorizeFunctions( + address target, + IAccess.FunctionInfo[] memory functions + ) + external + restricted() + onlyExistingTarget(target) + { + _checkComponentOrCustomTarget(target); + _authorizeTargetFunctions(target, ADMIN_ROLE(), functions, false); + } + + /// @dev locks the instance and all its releated targets including component and custom targets. function setInstanceLocked(bool locked) external @@ -351,15 +348,15 @@ contract InstanceAdmin is } } - - function toComponentRole(RoleId roleId, uint64 componentIdx) - internal - pure - returns (RoleId) - { - return RoleIdLib.toRoleId( - RoleIdLib.toInt(roleId) + componentIdx); - } + // TODO cleanup + // function toComponentRole(RoleId roleId, uint64 componentIdx) + // internal + // pure + // returns (RoleId) + // { + // return RoleIdLib.toRoleId( + // RoleIdLib.toInt(roleId) + componentIdx); + // } function _createTargetAuthorizations(IAuthorization authorization) @@ -377,4 +374,15 @@ contract InstanceAdmin is } } } + + + function _checkComponentOrCustomTarget(address target) + internal + view + { + IAccess.TargetType targetType = getTargetInfo(target).targetType; + if (targetType != TargetType.Component && targetType != TargetType.Custom) { + revert ErrorInstanceAdminNotComponentOrCustomTarget(target); + } + } } diff --git a/contracts/instance/InstanceAuthorizationV3.sol b/contracts/instance/InstanceAuthorizationV3.sol index 21b4fc186..f1d2115e9 100644 --- a/contracts/instance/InstanceAuthorizationV3.sol +++ b/contracts/instance/InstanceAuthorizationV3.sol @@ -148,6 +148,7 @@ contract InstanceAuthorizationV3 _authorize(functions, IInstance.revokeRole.selector, "revokeRole"); _authorize(functions, IInstance.createTarget.selector, "createTarget"); _authorize(functions, IInstance.authorizeFunctions.selector, "authorizeFunctions"); + _authorize(functions, IInstance.unauthorizeFunctions.selector, "unauthorizeFunctions"); // authorize instance service role functions = _authorizeForTarget(INSTANCE_TARGET_NAME, getServiceRole(INSTANCE())); @@ -169,6 +170,7 @@ contract InstanceAuthorizationV3 _authorize(functions, InstanceAdmin.createTarget.selector, "createTarget"); _authorize(functions, InstanceAdmin.authorizeFunctions.selector, "authorizeFunctions"); + _authorize(functions, InstanceAdmin.unauthorizeFunctions.selector, "unauthorizeFunctions"); _authorize(functions, InstanceAdmin.setTargetLocked.selector, "setTargetLocked"); _authorize(functions, InstanceAdmin.setInstanceLocked.selector, "setInstanceLocked"); diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index 9555bab44..c9c02859d 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -14,8 +14,8 @@ import {IOracle} from "../oracle/IOracle.sol"; import {IPolicy} from "../instance/module/IPolicy.sol"; import {IRegistry} from "../registry/IRegistry.sol"; import {IRisk} from "../instance/module/IRisk.sol"; -import {TimestampLib} from "../type/Timestamp.sol"; +import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {Amount} from "../type/Amount.sol"; import {BundleSet} from "./BundleSet.sol"; import {BUNDLE, COMPONENT, DISTRIBUTOR, DISTRIBUTION, FEE, PREMIUM, POLICY, POOL, PRODUCT} from "../type/ObjectType.sol"; @@ -628,6 +628,11 @@ contract InstanceReader { } + function toFunction(bytes4 signature, string memory name) public view returns (IAccess.FunctionInfo memory) { + return AccessAdminLib.toFunction(signature, name); + } + + function isLocked(address target) public view returns (bool) { return _instance.getInstanceAdmin().isTargetLocked(target); } diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index 60b9ff467..1f0f0bbeb 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -136,6 +136,20 @@ contract InstanceService is } + /// @inheritdoc IInstanceService + function unauthorizeFunctions( + address target, + IAccess.FunctionInfo[] memory functions + ) + external + restricted() + onlyInstance() + { + IInstance instance = IInstance(msg.sender); + return instance.getInstanceAdmin().unauthorizeFunctions(target, functions); + } + + /// @inheritdoc IInstanceService function setTargetLocked(address target, bool locked) external diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index 40059be0b..d664d934c 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -142,6 +142,7 @@ contract ServiceAuthorizationV3 _authorize(functions, IInstanceService.createTarget.selector, "createTarget"); _authorize(functions, IInstanceService.authorizeFunctions.selector, "authorizeFunctions"); + _authorize(functions, IInstanceService.unauthorizeFunctions.selector, "unauthorizeFunctions"); _authorize(functions, IInstanceService.setTargetLocked.selector, "setTargetLocked"); _authorize(functions, IInstanceService.setInstanceLocked.selector, "setInstanceLocked"); diff --git a/test/instance/authorization/InstanceAuthzTargets.t.sol b/test/instance/authorization/InstanceAuthzTargets.t.sol index 242743625..b66647921 100644 --- a/test/instance/authorization/InstanceAuthzTargets.t.sol +++ b/test/instance/authorization/InstanceAuthzTargets.t.sol @@ -137,9 +137,7 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { // WHEN - add function authz RoleId publicRoleId = instance.getInstanceAdmin().getPublicRole(); IAccess.FunctionInfo[] memory functions = new IAccess.FunctionInfo[](1); - functions[0] = AccessAdminLib.toFunction({ - name: "increaseCounter1", - selector: AccessManagedMock.increaseCounter1.selector}); + functions[0] = instanceReader.toFunction(AccessManagedMock.increaseCounter1.selector, "increaseCounter1"); vm.prank(instanceOwner); instance.authorizeFunctions(address(target), publicRoleId, functions); @@ -153,6 +151,67 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { } + function test_instanceAuthzTargetsUnauthorizeFunctionsHappyCase() public { + // GIVEN + AccessManagedMock target = _deployAccessManagedMock(); + string memory targetName = "MyTarget"; + RoleId publicRoleId = instance.getInstanceAdmin().getPublicRole(); + IAccess.FunctionInfo[] memory functions = new IAccess.FunctionInfo[](1); + functions[0] = instanceReader.toFunction(AccessManagedMock.increaseCounter1.selector, "increaseCounter1"); + + vm.startPrank(instanceOwner); + instance.createTarget(address(target), targetName); + instance.authorizeFunctions(address(target), publicRoleId, functions); + vm.stopPrank(); + + // must not revert + vm.prank(outsider); + target.increaseCounter1(); + + assertEq(target.counter1(), 1, "unexpected counter value after increaseCounter1"); + + // WHEN - unauthorize function + vm.prank(instanceOwner); + instance.unauthorizeFunctions(address(target), functions); + + // THEN + vm.expectRevert( + abi.encodeWithSelector( + IAccessManaged.AccessManagedUnauthorized.selector, + outsider)); + + vm.prank(outsider); + target.increaseCounter1(); + + assertEq(target.counter1(), 1, "unexpected counter value after increaseCounter1"); + } + + + function test_instanceAuthzToTargetAll() public { + bytes4 signature = AccessManagedMock.increaseCounter1.selector; + string memory name = "increaseCounter1"; + + IAccess.FunctionInfo memory myFunction = instanceReader.toFunction(signature, name); + assertEq(myFunction.name.toString(), name, "unexpected function name"); + assertEq(myFunction.selector.toBytes4(), signature, "unexpected function selector"); + assertEq(myFunction.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected function creation time"); + + // check signature zero check + vm.expectRevert( + abi.encodeWithSelector( + IAccessAdmin.ErrorAccessAdminSelectorZero.selector)); + + instanceReader.toFunction(bytes4(0), name); + + // check name length check + vm.expectRevert( + abi.encodeWithSelector( + IAccessAdmin.ErrorAccessAdminFunctionNameEmpty.selector)); + + instanceReader.toFunction(signature, ""); + } + + //--- helper functions ----------------------------------------------------// function _deployAccessManagedMock() internal returns (AccessManagedMock accessManagedMock) { From 2f7bd1b2b405e681279514ccc51d232d808245a9 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Tue, 3 Sep 2024 17:37:59 +0000 Subject: [PATCH 16/18] restructure and comment InstanceReader --- contracts/authorization/AccessAdmin.sol | 73 +- contracts/authorization/IAccessAdmin.sol | 5 +- contracts/instance/Instance.sol | 10 - contracts/instance/InstanceAdmin.sol | 25 +- contracts/instance/InstanceReader.sol | 696 ++++++++---------- contracts/instance/InstanceService.sol | 39 +- contracts/product/ClaimService.sol | 25 +- contracts/product/PolicyService.sol | 4 +- contracts/registry/RegistryAdmin.sol | 2 +- contracts/shared/ComponentService.sol | 27 +- contracts/shared/ContractLib.sol | 48 +- contracts/shared/IRegisterable.sol | 1 - contracts/staking/Staking.sol | 9 +- contracts/type/ObjectType.sol | 2 +- contracts/type/String.sol | 12 + contracts/upgradeability/ProxyManager.sol | 4 +- test/TestProduct.t.sol | 6 +- test/authorization/AccessAdmin.t.sol | 44 +- test/examples/fire/FireProduct.t.sol | 2 +- .../authorization/InstanceAuthzTargets.t.sol | 15 +- 20 files changed, 447 insertions(+), 602 deletions(-) diff --git a/contracts/authorization/AccessAdmin.sol b/contracts/authorization/AccessAdmin.sol index 4ac97a207..3fa6d8fa5 100644 --- a/contracts/authorization/AccessAdmin.sol +++ b/contracts/authorization/AccessAdmin.sol @@ -102,24 +102,6 @@ contract AccessAdmin is } - modifier onlyExistingRole( - RoleId roleId, - bool onlyActiveRole, - bool allowLockedRoles - ) - { - if (!allowLockedRoles) { - _checkRoleExists(roleId, onlyActiveRole); - } - _; - } - - - modifier onlyExistingTarget(address target) { - _checkTargetExists(target); - _; - } - //-------------- initialization functions ------------------------------// /// @dev Initializes this admin with the provided accessManager (and authorization specification). @@ -144,7 +126,7 @@ contract AccessAdmin is onlyDeployer() { // checks - // check authority is contract + // only contract check (authority might not yet be initialized at this time) if (!ContractLib.isContract(authority)) { revert ErrorAccessAdminAuthorityNotContract(authority); } @@ -226,19 +208,13 @@ contract AccessAdmin is return RoleId.wrap(_authority.PUBLIC_ROLE()); } - function roleExists(Str roleName) public view returns (bool exists) { - // special case admin adn public roles always exist - if (roleName == StrLib.toStr(ADMIN_ROLE_NAME()) || roleName == StrLib.toStr(PUBLIC_ROLE_NAME())) { + function roleExists(string memory name) public view returns (bool exists) { + // special case for admin and public roles + if (StrLib.eq(name, ADMIN_ROLE_NAME()) || StrLib.eq(name, PUBLIC_ROLE_NAME())) { return true; } - // check if a role id for the name exists - RoleId roleId = _roleForName[roleName].roleId; - if (roleId.eqz()) { - return false; - } - - return _roleInfo[roleId].createdAt.gtz(); + return _roleForName[StrLib.toStr(name)].roleId.gtz(); } function roleExists(RoleId roleId) public view returns (bool exists) { @@ -358,7 +334,9 @@ contract AccessAdmin is maxMemberCount: 1, name: ADMIN_ROLE_NAME()})); - // add this contract as admin role member + // add this contract as admin role member, as contract roles cannot be revoked + // and max member count is 1 for admin role this access admin contract will + // always be the only admin of the access manager. _roleMembers[ RoleIdLib.toRoleId(_authority.ADMIN_ROLE())].add(address(this)); @@ -442,9 +420,10 @@ contract AccessAdmin is bool addFunctions ) internal - onlyExistingTarget(target) - onlyExistingRole(roleId, true, !addFunctions) { + _checkTargetExists(target); + _checkRoleExists(roleId, true, true); + _authority.setTargetFunctionRole( target, AccessAdminLib.getSelectors(functions), @@ -491,8 +470,9 @@ contract AccessAdmin is /// @dev grant the specified role to the provided account function _grantRoleToAccount(RoleId roleId, address account) internal - onlyExistingRole(roleId, true, false) { + _checkRoleExists(roleId, true, false); + // check max role members will not be exceeded if (_roleMembers[roleId].length() >= _roleInfo[roleId].maxMemberCount) { revert ErrorAccessAdminRoleMembersLimitReached(roleId, _roleInfo[roleId].maxMemberCount); @@ -520,10 +500,10 @@ contract AccessAdmin is /// @dev revoke the specified role from the provided account function _revokeRoleFromAccount(RoleId roleId, address account) internal - onlyExistingRole(roleId, false, false) { + _checkRoleExists(roleId, false, false); - // check role removal is permitted + // check for attempt to revoke contract role if (_roleInfo[roleId].roleType == RoleType.Contract) { revert ErrorAccessAdminRoleMemberRemovalDisabled(roleId, account); } @@ -546,6 +526,11 @@ contract AccessAdmin is ) internal { + // skip admin and public roles (they are created during initialization) + if (roleId == ADMIN_ROLE() || roleId == PUBLIC_ROLE()) { + return; + } + AccessAdminLib.checkRoleCreation(this, roleId, info); _createRoleUnchecked(roleId, info); } @@ -716,8 +701,8 @@ contract AccessAdmin is function _setTargetLocked(address target, bool locked) internal - onlyExistingTarget(target) { + _checkTargetExists(target); _authority.setTargetClosed(target, locked); } @@ -752,7 +737,8 @@ contract AccessAdmin is function _checkRoleExists( RoleId roleId, - bool onlyActiveRole + bool onlyActiveRole, + bool allowAdminAndPublicRoles ) internal view @@ -761,9 +747,15 @@ contract AccessAdmin is revert ErrorAccessAdminRoleUnknown(roleId); } - uint64 roleIdInt = RoleId.unwrap(roleId); - if (roleIdInt == _authority.ADMIN_ROLE()) { - revert ErrorAccessAdminRoleIsLocked(roleId); + if (!allowAdminAndPublicRoles) { + if (roleId == ADMIN_ROLE()) { + revert ErrorAccessAdminInvalidUserOfAdminRole(); + } + + // check role is not public role + if (roleId == PUBLIC_ROLE()) { + revert ErrorAccessAdminInvalidUserOfPublicRole(); + } } // check if role is disabled @@ -773,7 +765,6 @@ contract AccessAdmin is } - /// @dev check if target exists and reverts if it doesn't function _checkTargetExists( address target diff --git a/contracts/authorization/IAccessAdmin.sol b/contracts/authorization/IAccessAdmin.sol index 7a032d8b2..b1264d5b0 100644 --- a/contracts/authorization/IAccessAdmin.sol +++ b/contracts/authorization/IAccessAdmin.sol @@ -39,7 +39,9 @@ interface IAccessAdmin is // only role owner modifier error ErrorAccessAdminNotRoleOwner(RoleId roleId, address account); - // only custom role modifier + // role management + error ErrorAccessAdminInvalidUserOfAdminRole(); + error ErrorAccessAdminInvalidUserOfPublicRole(); error ErrorAccessAdminRoleNotCustom(RoleId roleId); // initialization @@ -117,6 +119,7 @@ interface IAccessAdmin is function getPublicRole() external view returns (RoleId roleId); function roleExists(RoleId roleId) external view returns (bool exists); + function roleExists(string memory roleName) external view returns (bool exists); function getRoleForName(string memory name) external view returns (RoleId roleId); function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory roleInfo); function isRoleActive(RoleId roleId) external view returns (bool isActive); diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index 2300607ba..1f77d8406 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -342,14 +342,4 @@ contract Instance is function isTokenRegistryDisabled() external view returns (bool) { return _tokenRegistryDisabled; } - - //--- internal view/pure functions --------------------------------------// - - - function _checkCustomRole(RoleId roleId, bool exists) internal view { - if (!_instanceAdmin.isRoleCustom(roleId)) { - revert ErrorInstanceNotCustomRole(roleId); - } - } - } \ No newline at end of file diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index 258607c73..7570ed3e9 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -11,7 +11,7 @@ import {AccessAdmin} from "../authorization/AccessAdmin.sol"; import {AccessAdminLib} from "../authorization/AccessAdminLib.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; import {ObjectType, INSTANCE} from "../type/ObjectType.sol"; -import {RoleId, RoleIdLib, ADMIN_ROLE} from "../type/RoleId.sol"; +import {RoleId, ADMIN_ROLE} from "../type/RoleId.sol"; import {Str} from "../type/String.sol"; import {VersionPart} from "../type/Version.sol"; @@ -183,7 +183,7 @@ contract InstanceAdmin is /// @dev Creates a custom role. function createRole( - string memory roleName, + string memory name, RoleId adminRoleId, uint32 maxMemberCount ) @@ -191,6 +191,13 @@ contract InstanceAdmin is restricted() returns (RoleId roleId) { + // check role does not yet exist + if (roleExists(name)) { + revert ErrorAccessAdminRoleAlreadyCreated( + getRoleForName(name), + name); + } + // create roleId roleId = AccessAdminLib.getCustomRoleId(_customRoleIdNext++); @@ -201,7 +208,7 @@ contract InstanceAdmin is adminRoleId, IAccess.RoleType.Custom, maxMemberCount, - roleName)); + name)); } @@ -261,7 +268,6 @@ contract InstanceAdmin is ) external restricted() - onlyExistingTarget(target) { _checkComponentOrCustomTarget(target); _authorizeTargetFunctions(target, roleId, functions, true); @@ -275,7 +281,6 @@ contract InstanceAdmin is ) external restricted() - onlyExistingTarget(target) { _checkComponentOrCustomTarget(target); _authorizeTargetFunctions(target, ADMIN_ROLE(), functions, false); @@ -348,16 +353,6 @@ contract InstanceAdmin is } } - // TODO cleanup - // function toComponentRole(RoleId roleId, uint64 componentIdx) - // internal - // pure - // returns (RoleId) - // { - // return RoleIdLib.toRoleId( - // RoleIdLib.toInt(roleId) + componentIdx); - // } - function _createTargetAuthorizations(IAuthorization authorization) internal diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index c9c02859d..7c61fa942 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -21,6 +21,7 @@ import {BundleSet} from "./BundleSet.sol"; import {BUNDLE, COMPONENT, DISTRIBUTOR, DISTRIBUTION, FEE, PREMIUM, POLICY, POOL, PRODUCT} from "../type/ObjectType.sol"; import {ClaimId, ClaimIdLib} from "../type/ClaimId.sol"; import {DistributorType} from "../type/DistributorType.sol"; +import {InstanceAdmin} from "./InstanceAdmin.sol"; import {InstanceStore} from "./InstanceStore.sol"; import {Key32} from "../type/Key32.sol"; import {NftId} from "../type/NftId.sol"; @@ -37,7 +38,7 @@ import {UFixed, UFixedLib} from "../type/UFixed.sol"; /// @dev Central reader contract for a specific instance. -/// Should provide convenient reading functions for all instance and related component data. +/// Provides reading functions for all instance data and related component data. contract InstanceReader { error ErrorInstanceReaderAlreadyInitialized(); @@ -47,6 +48,7 @@ contract InstanceReader { IRegistry internal _registry; IInstance internal _instance; + InstanceAdmin internal _instanceAdmin; InstanceStore internal _store; BundleSet internal _bundleSet; @@ -62,6 +64,8 @@ contract InstanceReader { initializeWithInstance(msg.sender); } + + /// @dev Initializer to upgrade instance reader via instance service function initializeWithInstance(address instanceAddress) public { @@ -71,6 +75,7 @@ contract InstanceReader { _initialized = true; _instance = IInstance(instanceAddress); + _instanceAdmin = _instance.getInstanceAdmin(); _registry = _instance.getRegistry(); _store = _instance.getInstanceStore(); @@ -80,461 +85,347 @@ contract InstanceReader { } - // instance level functions + //--- instance functions ---------------------------------------------------------// + /// @dev Returns the registry this instance is registered in. function getRegistry() public view returns (IRegistry registry) { return _registry; } + + /// @dev Returns the instance NFT ID. function getInstanceNftId() public view returns (NftId instanceNftid) { return _registry.getNftIdForAddress(address(_instance)); } + + /// @dev Returns the instance contract. function getInstance() public view returns (IInstance instance) { return _instance; } + + //--- component functions ------------------------------------------------------// + + /// @dev Returns the number of registered components. + /// Components may be products, distributions, oracles or pools. function components() public view returns (uint256 componentCount) { - return _instance.getInstanceAdmin().components(); + return _instanceAdmin.components(); } - function products() public view returns (uint256 productCount) { - return _instance.products(); - } - function getProductNftId(uint256 idx) public view returns (NftId productNftId) { - return _instance.getProductNftId(idx); + /// @dev Returns the component info for the given component NFT ID. + function getComponentInfo(NftId componentNftId) public view returns (IComponents.ComponentInfo memory info) { + (bytes memory data, bool success) = _getData(_toComponentKey(componentNftId)); + if (success) { return abi.decode(data, (IComponents.ComponentInfo)); } } - // module specific functions - function getPolicyInfo(NftId policyNftId) - public - view - returns (IPolicy.PolicyInfo memory info) - { - bytes memory data = _store.getData(toPolicyKey(policyNftId)); - if (data.length > 0) { - return abi.decode(data, (IPolicy.PolicyInfo)); - } + /// @dev Returns the registered token for the given component NFT ID. + function getToken(NftId componentNftId) public view returns (IERC20Metadata token) { + TokenHandler tokenHandler = getTokenHandler(componentNftId); + if (address(tokenHandler) != address(0)) { return tokenHandler.TOKEN(); } } - function getPolicyState(NftId policyNftId) - public - view - returns (StateId state) - { - return _store.getState(toPolicyKey(policyNftId)); + + /// @dev Returns the current wallet address for the given component NFT ID. + /// The wallet address is either the component's own address or any other wallet address specified by the component owner. + /// The wallet holds the component's funds. Tokens collected by the component are transferred to the wallet and + /// Tokens distributed from the component are transferred from this wallet. + function getWallet(NftId componentNftId) public view returns (address wallet) { + TokenHandler tokenHandler = getTokenHandler(componentNftId); + if (address(tokenHandler) != address(0)) { return tokenHandler.getWallet(); } } - function getPremiumInfo(NftId policyNftId) - public - view - returns (IPolicy.PremiumInfo memory info) - { - bytes memory data = _store.getData(toPremiumKey(policyNftId)); - if (data.length > 0) { - return abi.decode(data, (IPolicy.PremiumInfo)); - } + + /// @dev Returns the token handler for the given component NFT ID. + /// The token handler manages all transfers from/to the component's wallet. + /// To allow a component to collect funds from an account, it has to create a corresponding allowance from the + /// account to the address of the component's token handler. + function getTokenHandler(NftId componentNftId) public view returns (TokenHandler tokenHandler) { + (bytes memory data, bool success) = _getData(_toComponentKey(componentNftId)); + if (success) { return abi.decode(data, (IComponents.ComponentInfo)).tokenHandler; } } - function getPremiumInfoState(NftId policyNftId) - public - view - returns (StateId state) - { - return _store.getState(toPremiumKey(policyNftId)); + + /// @dev Returns the current token balance amount for the given component NFT ID. + /// The balance amount includes the fee amount. + function getBalanceAmount(NftId targetNftId) external view returns (Amount) { + return _store.getBalanceAmount(targetNftId); } - function bundles(NftId poolNftId) - public - view - returns (uint256 bundleCount) - { - return _bundleSet.bundles(poolNftId); + + /// @dev Returns the current fee amount for the given NFT ID. + /// The target NFT ID may reference a component, a distributor or a bundle. + function getFeeAmount(NftId targetNftId) external view returns (Amount) { + return _store.getFeeAmount(targetNftId); } - function activeBundles(NftId poolNftId) - public - view - returns (uint256 bundleCount) - { - return _bundleSet.activeBundles(poolNftId); + + /// @dev Returns the currently locked amount for the given NFT ID. + /// The target NFT ID may reference a pool or a bundle. + function getLockedAmount(NftId targetNftId) external view returns (Amount) { + return _store.getLockedAmount(targetNftId); } - function getActiveBundleNftId(NftId poolNftId, uint256 idx) - public - view - returns (NftId bundleNftId) - { - return _bundleSet.getActiveBundleNftId(poolNftId, idx); + //--- product functions ------------------------------------------------------// + + /// @dev Returns the number of registered products. + function products() public view returns (uint256 productCount) { + return _instance.products(); } - function getBundleNftId(NftId poolNftId, uint256 idx) - public - view - returns (NftId bundleNftId) - { - return _bundleSet.getBundleNftId(poolNftId, idx); + + /// @dev Returns th product NFT ID for the given index. + function getProductNftId(uint256 idx) public view returns (NftId productNftId) { + return _instance.getProductNftId(idx); } - function getBundleState(NftId bundleNftId) - public - view - returns (StateId state) - { - return _store.getState(toBundleKey(bundleNftId)); + + /// @dev Returns the product info for the given product NFT ID. + function getProductInfo(NftId productNftId) public view returns (IComponents.ProductInfo memory info) { + (bytes memory data, bool success) = _getData(productNftId.toKey32(PRODUCT())); + if (success) { return abi.decode(data, (IComponents.ProductInfo)); } } - /// @dev Returns true iff policy is active. - function policyIsActive(NftId policyNftId) - public - view - returns (bool isCloseable) - { - return PolicyServiceLib.policyIsActive(this, policyNftId); + + /// @dev Returns the current fee settings for the given product NFT ID. + function getFeeInfo(NftId productNftId) public view returns (IComponents.FeeInfo memory feeInfo) { + (bytes memory data, bool success) = _getData(productNftId.toKey32(FEE())); + if (success) { return abi.decode(data, (IComponents.FeeInfo)); } } - function claims(NftId policyNftId) - public - view - returns (uint16 claimCount) - { - return getPolicyInfo(policyNftId).claimsCount; + //--- risk functions ---------------------------------------------------------// + + /// @dev Returns the total number of registered risks for the specified product. + function risks(NftId productNftId) public view returns (uint256 riskCount) { + return _riskSet.risks(productNftId); } - function getClaimId(uint idx) - public - pure - returns (ClaimId claimId) - { - return ClaimIdLib.toClaimId(idx + 1); + /// @dev Returns the number of active risks for the specified product. + function activeRisks(NftId productNftId) public view returns (uint256 activeRiskCount) { + return _riskSet.activeRisks(productNftId); } - function getClaimInfo(NftId policyNftId, ClaimId claimId) - public - view - returns (IPolicy.ClaimInfo memory info) - { - bytes memory data = _store.getData(claimId.toKey32(policyNftId)); - if (data.length > 0) { - return abi.decode(data, (IPolicy.ClaimInfo)); - } + + /// @dev Returns the risk ID for the given product NFT ID and (registered) risk index. + function getRiskId(NftId productNftId, uint256 idx) public view returns (RiskId riskId) { + return _riskSet.getRiskId(productNftId, idx); } - function getClaimState(NftId policyNftId, ClaimId claimId) - public - view - returns (StateId state) - { - return _store.getState(claimId.toKey32(policyNftId)); + /// @dev Returns the active risk ID for the given product NFT ID and (active) risk index. + function getActiveRiskId(NftId productNftId, uint256 idx) public view returns (RiskId riskId) { + return _riskSet.getActiveRiskId(productNftId, idx); } - function payouts(NftId policyNftId, ClaimId claimId) - public - view - returns (uint24 payoutCount) - { - return getClaimInfo(policyNftId, claimId).payoutsCount; + /// @dev Returns the risk info for the given risk ID. + function getRiskInfo(RiskId riskId) public view returns (IRisk.RiskInfo memory info) { + (bytes memory data, bool success) = _getData(riskId.toKey32()); + if (success) { return abi.decode(data, (IRisk.RiskInfo)); } } - function getPayoutId(ClaimId claimId, uint24 idx) - public - pure - returns (PayoutId payoutId) - { - return PayoutIdLib.toPayoutId(claimId, idx + 1); + /// @dev Returns the risk state for the given risk ID. + function getRiskState(RiskId riskId) public view returns (StateId stateId) { + return getState(riskId.toKey32()); } - function getRemainingClaimableAmount(NftId policyNftId) - public - view - returns (Amount remainingClaimableAmount) - { - IPolicy.PolicyInfo memory info = getPolicyInfo(policyNftId); - return info.sumInsuredAmount - info.claimAmount; + //--- policy functions -------------------------------------------------------// + + /// @dev Returns the number of linked policies for the given risk ID. + function policiesForRisk(RiskId riskId) public view returns (uint256 linkedPolicies) { + return _riskSet.linkedPolicies(riskId); } - function getPayoutInfo(NftId policyNftId, PayoutId payoutId) - public - view - returns (IPolicy.PayoutInfo memory info) - { - bytes memory data = _store.getData(payoutId.toKey32(policyNftId)); - if (data.length > 0) { - return abi.decode(data, (IPolicy.PayoutInfo)); - } + + /// @dev Returns the linked policy NFT ID for the given risk ID and index. + function getPolicyNftIdForRisk(RiskId riskId, uint256 idx) public view returns (NftId linkedPolicyNftId) { + return _riskSet.getLinkedPolicyNftId(riskId, idx); } - function getPayoutState(NftId policyNftId, PayoutId payoutId) - public - view - returns (StateId state) - { - return _store.getState(payoutId.toKey32(policyNftId)); + + /// @dev Returns the info for the given policy NFT ID. + function getPolicyInfo(NftId policyNftId) public view returns (IPolicy.PolicyInfo memory info) { + (bytes memory data, bool success) = _getData(_toPolicyKey(policyNftId)); + if (success) { return abi.decode(data, (IPolicy.PolicyInfo)); } } - function risks(NftId productNftId) - public - view - returns (uint256 riskCount) - { - return _riskSet.risks(productNftId); + + /// @dev Returns the state for the given policy NFT ID. + function getPolicyState(NftId policyNftId) public view returns (StateId state) { + return getState(_toPolicyKey(policyNftId)); } - function getRiskId(NftId productNftId, uint256 idx) - public - view - returns (RiskId riskId) - { - return _riskSet.getRiskId(productNftId, idx); + + /// @dev Returns true iff policy is active. + function policyIsActive(NftId policyNftId) public view returns (bool isCloseable) { + return PolicyServiceLib.policyIsActive(this, policyNftId); } - function activeRisks(NftId productNftId) - public - view - returns (uint256 activeRiskCount) - { - return _riskSet.activeRisks(productNftId); + //--- claim functions -------------------------------------------------------// + + /// @dev Returns the number of claims for the given policy NFT ID. + function claims(NftId policyNftId) public view returns (uint16 claimCount) { + return getPolicyInfo(policyNftId).claimsCount; } - function getActiveRiskId(NftId productNftId, uint256 idx) - public - view - returns (RiskId riskId) - { - return _riskSet.getActiveRiskId(productNftId, idx); + + /// @dev Returns the claim ID for the given policy NFT ID and index. + function getClaimId(uint256 idx) public pure returns (ClaimId claimId) { + return ClaimIdLib.toClaimId(idx + 1); } - function getRiskInfo(RiskId riskId) - public - view - returns (IRisk.RiskInfo memory info) - { - bytes memory data = _store.getData(riskId.toKey32()); - if (data.length > 0) { - return abi.decode(data, (IRisk.RiskInfo)); + + /// @dev Returns the claim info for the given policy NFT ID and claim ID. + function getClaimInfo(NftId policyNftId, ClaimId claimId) public view returns (IPolicy.ClaimInfo memory info) { + (bytes memory data, bool success) = _getData(claimId.toKey32(policyNftId)); + if (success) { + return abi.decode(data, (IPolicy.ClaimInfo)); } } - function getRiskState(RiskId riskId) - public - view - returns (StateId stateId) - { - bytes memory data = _store.getData(riskId.toKey32()); - return _store.getState(riskId.toKey32()); - } - function policiesForRisk(RiskId riskId) - public - view - returns (uint256 linkedPolicies) - { - return _riskSet.linkedPolicies(riskId); + /// @dev Returns the current claim state for the given policy NFT ID and claim ID. + function getClaimState(NftId policyNftId, ClaimId claimId) public view returns (StateId state) { + return getState(claimId.toKey32(policyNftId)); } - function getPolicyNftIdForRisk(RiskId riskId, uint256 idx) - public - view - returns (NftId linkedPolicyNftId) - { - return _riskSet.getLinkedPolicyNftId(riskId, idx); + + /// @dev Returns the remaining claimable amount for the given policy NFT ID. + /// The remaining claimable amount is the difference between the sum insured amount and total approved claim amounts so far. + function getRemainingClaimableAmount(NftId policyNftId) + public view returns (Amount remainingClaimableAmount) { + IPolicy.PolicyInfo memory info = getPolicyInfo(policyNftId); + return info.sumInsuredAmount - info.claimAmount; } + //--- payout functions -------------------------------------------------------// - function getToken(NftId componentNftId) - public - view - returns (IERC20Metadata token) - { - TokenHandler tokenHandler = getTokenHandler(componentNftId); - if (address(tokenHandler) != address(0)) { - return tokenHandler.TOKEN(); - } + /// @dev Returns the number of payouts for the given policy NFT ID and claim ID. + function payouts(NftId policyNftId, ClaimId claimId) public view returns (uint24 payoutCount) { + return getClaimInfo(policyNftId, claimId).payoutsCount; } - function getWallet(NftId componentNftId) - public - view - returns (address wallet) - { - TokenHandler tokenHandler = getTokenHandler(componentNftId); - if (address(tokenHandler) != address(0)) { - return tokenHandler.getWallet(); - } + /// @dev Returns the payout ID for the given claim ID and index. + function getPayoutId(ClaimId claimId, uint24 idx) public pure returns (PayoutId payoutId) { + return PayoutIdLib.toPayoutId(claimId, idx + 1); } - function getTokenHandler(NftId componentNftId) - public - view - returns (TokenHandler tokenHandler) - { - bytes memory data = _store.getData(toComponentKey(componentNftId)); - if (data.length > 0) { - return abi.decode(data, (IComponents.ComponentInfo)).tokenHandler; - } + /// @dev Returns the payout info for the given policy NFT ID and payout ID. + function getPayoutInfo(NftId policyNftId, PayoutId payoutId) public view returns (IPolicy.PayoutInfo memory info) { + (bytes memory data, bool success) = _getData(payoutId.toKey32(policyNftId)); + if (success) { return abi.decode(data, (IPolicy.PayoutInfo)); } } - function getBundleInfo(NftId bundleNftId) - public - view - returns (IBundle.BundleInfo memory info) - { - bytes memory data = _store.getData(toBundleKey(bundleNftId)); - if (data.length > 0) { - return abi.decode(data, (IBundle.BundleInfo)); - } + /// @dev Returns the payout state for the given policy NFT ID and payout ID. + function getPayoutState(NftId policyNftId, PayoutId payoutId) public view returns (StateId state) { + return getState(payoutId.toKey32(policyNftId)); } - function getDistributorTypeInfo(DistributorType distributorType) - public - view - returns (IDistribution.DistributorTypeInfo memory info) - { - bytes memory data = _store.getData(distributorType.toKey32()); - if (data.length > 0) { - return abi.decode(data, (IDistribution.DistributorTypeInfo)); - } + //--- premium functions -------------------------------------------------------// + + /// @dev Returns the premium info for the given policy NFT ID. + function getPremiumInfo(NftId policyNftId) public view returns (IPolicy.PremiumInfo memory info) { + (bytes memory data, bool success) = _getData(_toPremiumKey(policyNftId)); + if (success) { return abi.decode(data, (IPolicy.PremiumInfo)); } } - function getDistributorInfo(NftId distributorNftId) - public - view - returns (IDistribution.DistributorInfo memory info) - { - bytes memory data = _store.getData(toDistributorKey(distributorNftId)); - if (data.length > 0) { - return abi.decode(data, (IDistribution.DistributorInfo)); - } + + /// @dev Returns the premium state for the given policy NFT ID. + function getPremiumState(NftId policyNftId) public view returns (StateId state) { + return getState(_toPremiumKey(policyNftId)); } - function getBalanceAmount(NftId targetNftId) external view returns (Amount) { - return _store.getBalanceAmount(targetNftId); + //--- oracle functions ---------------------------------------------------------// + + /// @dev Returns the request info for the given oracle request ID. + function getRequestInfo(RequestId requestId) public view returns (IOracle.RequestInfo memory requestInfo) { + (bytes memory data, bool success) = _getData(requestId.toKey32()); + if (success) { return abi.decode(data, (IOracle.RequestInfo)); } } - function getLockedAmount(NftId targetNftId) external view returns (Amount) { - return _store.getLockedAmount(targetNftId); + //--- pool functions -----------------------------------------------------------// + + /// @dev Returns the pool info for the given pool NFT ID. + function getPoolInfo(NftId poolNftId) public view returns (IComponents.PoolInfo memory info) { + (bytes memory data, bool success) = _getData(poolNftId.toKey32(POOL())); + if (success) { return abi.decode(data, (IComponents.PoolInfo)); } } - function getFeeAmount(NftId targetNftId) external view returns (Amount) { - return _store.getFeeAmount(targetNftId); + //--- bundle functions -------------------------------------------------------// + + /// @dev Returns the total number of registered bundles for the given pool. + function bundles(NftId poolNftId) public view returns (uint256 bundleCount) { + return _bundleSet.bundles(poolNftId); } - function getComponentInfo(NftId componentNftId) - public - view - returns (IComponents.ComponentInfo memory info) - { - bytes memory data = _store.getData(toComponentKey(componentNftId)); - if (data.length > 0) { - return abi.decode(data, (IComponents.ComponentInfo)); - } + + /// @dev Returns the number of active bundles for the given pool. + function activeBundles(NftId poolNftId) public view returns (uint256 bundleCount) { + return _bundleSet.activeBundles(poolNftId); } - function getProductInfo(NftId productNftId) - public - view - returns (IComponents.ProductInfo memory info) - { - bytes memory data = _store.getData(toProductKey(productNftId)); - if (data.length > 0) { - return abi.decode(data, (IComponents.ProductInfo)); - } + + /// @dev Returns the bunde NFT ID for the given pool and index. + function getBundleNftId(NftId poolNftId, uint256 idx) public view returns (NftId bundleNftId) { + return _bundleSet.getBundleNftId(poolNftId, idx); } - function getFeeInfo(NftId productNftId) - public - view - returns (IComponents.FeeInfo memory feeInfo) - { - bytes memory data = _store.getData(toFeeKey(productNftId)); - if (data.length > 0) { - return abi.decode(data, (IComponents.FeeInfo)); - } + + /// @dev Returns the active bunde NFT ID for the given pool and index. + function getActiveBundleNftId(NftId poolNftId, uint256 idx) public view returns (NftId bundleNftId) { + return _bundleSet.getActiveBundleNftId(poolNftId, idx); } - function getPoolInfo(NftId poolNftId) - public - view - returns (IComponents.PoolInfo memory info) - { - bytes memory data = _store.getData(toPoolKey(poolNftId)); - if (data.length > 0) { - return abi.decode(data, (IComponents.PoolInfo)); - } + + /// @dev Returns the bundle info for the given bundle NFT ID. + function getBundleInfo(NftId bundleNftId) public view returns (IBundle.BundleInfo memory info) { + (bytes memory data, bool success) = _getData(_toBundleKey(bundleNftId)); + if (success) { return abi.decode(data, (IBundle.BundleInfo)); } } - function getReferralInfo(ReferralId referralId) - public - view - returns (IDistribution.ReferralInfo memory info) - { - bytes memory data = _store.getData(referralId.toKey32()); - if (data.length > 0) { - return abi.decode(data, (IDistribution.ReferralInfo)); - } + + /// @dev Returns the bundle state for the given bundle NFT ID. + function getBundleState(NftId bundleNftId) public view returns (StateId state) { + return getState(_toBundleKey(bundleNftId)); } - function isReferralValid(NftId distributionNftId, ReferralId referralId) - external - view - returns (bool isValid) - { - return _distributionService.referralIsValid( - distributionNftId, - referralId); + //--- distribution functions -------------------------------------------------------// + + function getDistributorTypeInfo(DistributorType distributorType) public view returns (IDistribution.DistributorTypeInfo memory info) { + (bytes memory data, bool success) = _getData(distributorType.toKey32()); + if (success) { return abi.decode(data, (IDistribution.DistributorTypeInfo)); } } - function getRequestInfo(RequestId requestId) - public - view - returns (IOracle.RequestInfo memory requestInfo) - { - bytes memory data = _store.getData(requestId.toKey32()); - if (data.length > 0) { - return abi.decode(data, (IOracle.RequestInfo)); - } + + function getDistributorInfo(NftId distributorNftId) public view returns (IDistribution.DistributorInfo memory info) { + (bytes memory data, bool success) = _getData(distributorNftId.toKey32(DISTRIBUTOR())); + if (success) { return abi.decode(data, (IDistribution.DistributorInfo)); } } - function getMetadata(Key32 key) - public - view - returns (IKeyValueStore.Metadata memory metadata) - { - return _store.getMetadata(key); + //--- referral functions -------------------------------------------------------// + + function toReferralId(NftId distributionNftId, string memory referralCode) public pure returns (ReferralId referralId) { + return ReferralLib.toReferralId(distributionNftId, referralCode); } - function getState(Key32 key) - public - view - returns (StateId state) - { - return _store.getMetadata(key).state; + function isReferralValid(NftId distributionNftId, ReferralId referralId) external view returns (bool isValid) { + return _distributionService.referralIsValid(distributionNftId, referralId); } - function toReferralId( - NftId distributionNftId, - string memory referralCode - ) - public - pure - returns (ReferralId referralId) - { - return ReferralLib.toReferralId( - distributionNftId, - referralCode); + function getReferralInfo(ReferralId referralId) public view returns (IDistribution.ReferralInfo memory info) { + (bytes memory data, bool success) = _getData(referralId.toKey32()); + if (success) { return abi.decode(data, (IDistribution.ReferralInfo)); } } @@ -554,157 +445,188 @@ contract InstanceReader { referralId); } + //--- authorization functions -------------------------------------------------------// + /// @dev Returns the number of defined roles. function roles() public view returns (uint256) { - return _instance.getInstanceAdmin().roles(); + return _instanceAdmin.roles(); } + /// @dev Returns the role ID for the given index. + function getRoleId(uint256 idx) public view returns (RoleId roleId) { + return _instanceAdmin.getRoleId(uint64(idx)); + } + + + /// @dev Returns the role ID for the instance owner role. + /// This role may be used as a "root" admin role for other custom roles defined for this instance. function getInstanceOwnerRole() public pure returns (RoleId roleId) { return INSTANCE_OWNER_ROLE(); } - function getRoleId(uint256 idx) public view returns (RoleId roleId) { - return _instance.getInstanceAdmin().getRoleId(uint64(idx)); + /// @dev Returns the role info for the given role ID. + function getRoleInfo(RoleId roleId) public view returns (IAccess.RoleInfo memory roleInfo) { + return _instanceAdmin.getRoleInfo(roleId); } - function roleExists(RoleId roleId) public view returns (bool exists) { - return _instance.getInstanceAdmin().roleExists(roleId); - } - function getRoleInfo(RoleId roleId) public view returns (IAccess.RoleInfo memory roleInfo) { - return _instance.getInstanceAdmin().getRoleInfo(roleId); + /// @dev Returns true iff the provided role ID is defined for this instance. + function roleExists(RoleId roleId) public view returns (bool exists) { + return _instanceAdmin.roleExists(roleId); } + /// @dev Returns true iff the provided role ID represents a custom role ID. function isRoleCustom(RoleId roleId) public view returns (bool isCustom) { - return _instance.getInstanceAdmin().isRoleCustom(roleId); + return _instanceAdmin.isRoleCustom(roleId); } + /// @dev Returns true iff the provided role ID is active. function isRoleActive(RoleId roleId) public view returns (bool isActive) { - return _instance.getInstanceAdmin().isRoleActive(roleId); + return _instanceAdmin.isRoleActive(roleId); } + /// @dev Returns the number of members (accounts) for the given role ID. function roleMembers(RoleId roleId) public view returns (uint256 numberOfMembers) { - return _instance.getInstanceAdmin().roleMembers(roleId); + return _instanceAdmin.roleMembers(roleId); } + /// @dev Returns the member (account address) for the given role ID and index. function getRoleMember(RoleId roleId, uint256 idx) public view returns (address account) { - return _instance.getInstanceAdmin().getRoleMember(roleId, idx); + return _instanceAdmin.getRoleMember(roleId, idx); } + /// @dev Returns true iff the given account is a member of the specified role ID. function isRoleMember(RoleId roleId, address account) public view returns (bool isMember) { - return _instance.getInstanceAdmin().isRoleMember(roleId, account); + return _instanceAdmin.isRoleMember(roleId, account); } + /// @dev Returns true iff the given account is an admin of the specified role ID. + /// Role admins may grant and revoke the role to other accounts. function isRoleAdmin(RoleId roleId, address account) public view returns (bool isMember) { - return _instance.getInstanceAdmin().isRoleAdmin(roleId, account); + return _instanceAdmin.isRoleAdmin(roleId, account); } + /// @dev Returns the number of targets (contracts) defined for this instance. function targets() public view returns (uint256 targetCount) { - return _instance.getInstanceAdmin().targets(); + return _instanceAdmin.targets(); } + /// @dev Returns the target address for the given index. function getTargetAddress(uint256 idx) public view returns (address target) { - return _instance.getInstanceAdmin().getTargetAddress(idx); + return _instanceAdmin.getTargetAddress(idx); } - function targetExists(address target) public view returns (bool exists) { - return _instance.getInstanceAdmin().targetExists(target); + /// @dev Returns the target info for the given target address. + function getTargetInfo(address target) public view returns (IAccess.TargetInfo memory targetInfo) { + return _instanceAdmin.getTargetInfo(target); } - function getTargetInfo(address target) public view returns (IAccess.TargetInfo memory targetInfo) { - return _instance.getInstanceAdmin().getTargetInfo(target); + /// @dev Returns true iff the given target is defined for this instance. + function targetExists(address target) public view returns (bool exists) { + return _instanceAdmin.targetExists(target); } - function toFunction(bytes4 signature, string memory name) public view returns (IAccess.FunctionInfo memory) { - return AccessAdminLib.toFunction(signature, name); + /// @dev Returns true iff the given target is locked. + function isLocked(address target) public view returns (bool) { + return _instanceAdmin.isTargetLocked(target); } - function isLocked(address target) public view returns (bool) { - return _instance.getInstanceAdmin().isTargetLocked(target); + /// @dev Returns the number of authorized functions for the given target. + function authorizedFunctions(address target) external view returns (uint256 numberOfFunctions) { + return _instanceAdmin.authorizedFunctions(target); } - function toPolicyKey(NftId policyNftId) public pure returns (Key32) { - return policyNftId.toKey32(POLICY()); + /// @dev Returns the authorized function info for the given target and index. + function getAuthorizedFunction(address target, uint256 idx) external view returns (IAccess.FunctionInfo memory func, RoleId roleId) { + return _instanceAdmin.getAuthorizedFunction(target, idx); } - function toPremiumKey(NftId policyNftId) public pure returns (Key32) { - return policyNftId.toKey32(PREMIUM()); + /// @dev Returns a function info for the given function signature and function name. + /// The function signature must not be zero and the function name must not be empty. + function toFunction(bytes4 signature, string memory name) public view returns (IAccess.FunctionInfo memory) { + return AccessAdminLib.toFunction(signature, name); } + //--- low level function ----------------------------------------------------// - function toDistributorKey(NftId distributorNftId) public pure returns (Key32) { - return distributorNftId.toKey32(DISTRIBUTOR()); + function getInstanceAdmin() external view returns (InstanceAdmin instanceAdmin) { + return _instanceAdmin; } + function getInstanceStore() external view returns (IKeyValueStore store) { + return _store; + } - function toBundleKey(NftId poolNftId) public pure returns (Key32) { - return poolNftId.toKey32(BUNDLE()); + + function getBundleSet() external view returns (BundleSet bundleSet) { + return _bundleSet; } - function toComponentKey(NftId componentNftId) public pure returns (Key32) { - return componentNftId.toKey32(COMPONENT()); + function getRiskSet() external view returns (RiskSet riskSet) { + return _riskSet; } - function toDistributionKey(NftId distributionNftId) public pure returns (Key32) { - return distributionNftId.toKey32(DISTRIBUTION()); + function getMetadata(Key32 key) public view returns (IKeyValueStore.Metadata memory metadata) { + return _store.getMetadata(key); } - function toPoolKey(NftId poolNftId) public pure returns (Key32) { - return poolNftId.toKey32(POOL()); + function getState(Key32 key) public view returns (StateId state) { + return _store.getState(key); } - function toProductKey(NftId productNftId) public pure returns (Key32) { - return productNftId.toKey32(PRODUCT()); + function toUFixed(uint256 value, int8 exp) public pure returns (UFixed) { + return UFixedLib.toUFixed(value, exp); } - function toFeeKey(NftId productNftId) public pure returns (Key32) { - return productNftId.toKey32(FEE()); + function toInt(UFixed value) public pure returns (uint256) { + return UFixedLib.toInt(value); } - //--- low level function ----------------------------------------------------// + //--- internal functions ----------------------------------------------------// - function getInstanceStore() external view returns (IKeyValueStore store) { - return _store; + function _getData(Key32 key) internal view returns (bytes memory data, bool success) { + data = _store.getData(key); + return (data, data.length > 0); } - function getBundleSet() external view returns (BundleSet bundleSet) { - return _bundleSet; + function _toPolicyKey(NftId policyNftId) internal pure returns (Key32) { + return policyNftId.toKey32(POLICY()); } - function getRiskSet() external view returns (RiskSet riskSet) { - return _riskSet; + function _toPremiumKey(NftId policyNftId) internal pure returns (Key32) { + return policyNftId.toKey32(PREMIUM()); } - function toUFixed(uint256 value, int8 exp) public pure returns (UFixed) { - return UFixedLib.toUFixed(value, exp); + function _toBundleKey(NftId poolNftId) internal pure returns (Key32) { + return poolNftId.toKey32(BUNDLE()); } - function toInt(UFixed value) public pure returns (uint256) { - return UFixedLib.toInt(value); + function _toComponentKey(NftId componentNftId) internal pure returns (Key32) { + return componentNftId.toKey32(COMPONENT()); } } \ No newline at end of file diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index 1f0f0bbeb..8bf5c3cba 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -279,7 +279,7 @@ contract InstanceService is returns (Amount newBalance) { NftId instanceNftId = getRegistry().getNftIdForAddress(msg.sender); - _stakingService.withdrawInstanceRewardReserves( + newBalance = _stakingService.withdrawInstanceRewardReserves( instanceNftId, dipAmount); } @@ -426,37 +426,7 @@ contract InstanceService is } - /// all gif targets MUST be children of instanceNftId - function _createGifTarget( - NftId instanceNftId, - address targetAddress, - string memory targetName, - RoleId[] memory roles, - bytes4[][] memory selectors - ) - internal - virtual - { - // TODO instanceAdmin will check target instance match anyway - ( - IInstance instance, // or instanceInfo - // or targetInfo - ) = _validateInstanceAndComponent(instanceNftId, targetAddress); - - InstanceAdmin instanceAdmin = instance.getInstanceAdmin(); - - // TODO refactor/implement - // instanceAdmin.createGifTarget(targetAddress, targetName); - - // set proposed target config - for(uint roleIdx = 0; roleIdx < roles.length; roleIdx++) { - // TODO refactor/implement - // instanceAdmin.setTargetFunctionRoleByService(targetName, selectors[roleIdx], roles[roleIdx]); - } - } - - - /// @dev top level initializer + /// @dev top level initializer (upgradable contract) function _initialize( address owner, bytes memory data @@ -499,12 +469,9 @@ contract InstanceService is } componentNftId = componentInfo.nftId; - } else { - } - instance = Instance(instanceInfo.objectAddress); - + instance = Instance(instanceInfo.objectAddress); } diff --git a/contracts/product/ClaimService.sol b/contracts/product/ClaimService.sol index c59b1aeb8..2f2a21166 100644 --- a/contracts/product/ClaimService.sol +++ b/contracts/product/ClaimService.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import {IClaimService} from "./IClaimService.sol"; -import {IComponents} from "../instance/module/IComponents.sol"; import {IInstance} from "../instance/IInstance.sol"; import {IPolicy} from "../instance/module/IPolicy.sol"; import {IPolicyHolder} from "../shared/IPolicyHolder.sol"; @@ -14,11 +13,10 @@ import {IRegistry} from "../registry/IRegistry.sol"; import {Amount, AmountLib} from "../type/Amount.sol"; import {ClaimId, ClaimIdLib} from "../type/ClaimId.sol"; import {ContractLib} from "../shared/ContractLib.sol"; -import {FeeLib} from "../type/Fee.sol"; import {InstanceReader} from "../instance/InstanceReader.sol"; import {InstanceStore} from "../instance/InstanceStore.sol"; import {NftId} from "../type/NftId.sol"; -import {ObjectType, COMPONENT, CLAIM, POLICY, POOL, PRODUCT} from "../type/ObjectType.sol"; +import {ObjectType, CLAIM, POLICY, POOL, PRODUCT} from "../type/ObjectType.sol"; import {PayoutId, PayoutIdLib} from "../type/PayoutId.sol"; import {Service} from "../shared/Service.sol"; import {StateId} from "../type/StateId.sol"; @@ -169,8 +167,7 @@ contract ClaimService is _checkNftType(policyNftId, POLICY()); ( - , - IInstance instance, + ,, InstanceReader instanceReader, InstanceStore instanceStore, IPolicy.PolicyInfo memory policyInfo @@ -200,8 +197,7 @@ contract ClaimService is // nonReentrant() // prevents creating a reinsurance claim in a single tx { ( - , - IInstance instance, + ,, InstanceReader instanceReader, InstanceStore instanceStore, IPolicy.PolicyInfo memory policyInfo @@ -235,7 +231,6 @@ contract ClaimService is ,, InstanceReader instanceReader, InstanceStore instanceStore, - IPolicy.PolicyInfo memory policyInfo ) = _verifyCallerWithPolicy(policyNftId); // check/update claim info @@ -323,8 +318,7 @@ contract ClaimService is { // checks ( - NftId productNftId,, - // IInstance instance, + ,, InstanceReader instanceReader, InstanceStore instanceStore, IPolicy.PolicyInfo memory policyInfo @@ -356,7 +350,8 @@ contract ClaimService is Amount payoutAmount = payoutInfo.amount; { ClaimId claimId = payoutId.toClaimId(); - IPolicy.ClaimInfo memory claimInfo = instanceReader.getClaimInfo(policyNftId, claimId); + // TODO cleanup + // IPolicy.ClaimInfo memory claimInfo = instanceReader.getClaimInfo(policyNftId, claimId); claimInfo.paidAmount = claimInfo.paidAmount.add(payoutAmount); claimInfo.openPayoutsCount -= 1; @@ -401,11 +396,9 @@ contract ClaimService is { // checks ( - , - IInstance instance, + ,, InstanceReader instanceReader, InstanceStore instanceStore, - IPolicy.PolicyInfo memory policyInfo ) = _verifyCallerWithPolicy(policyNftId); StateId payoutState = instanceReader.getPayoutState(policyNftId, payoutId); @@ -528,7 +521,7 @@ contract ClaimService is IPolicy.PolicyInfo memory policyInfo ) { - (productNftId, instance) = _getAndVerifyActiveComponent(PRODUCT()); + (productNftId, instance) = _getAndVerifyActiveProduct(); instanceReader = instance.getInstanceReader(); instanceStore = instance.getInstanceStore(); @@ -542,7 +535,7 @@ contract ClaimService is } - function _getAndVerifyActiveComponent(ObjectType expectedType) + function _getAndVerifyActiveProduct() internal view returns ( diff --git a/contracts/product/PolicyService.sol b/contracts/product/PolicyService.sol index 178d36d66..89786fd87 100644 --- a/contracts/product/PolicyService.sol +++ b/contracts/product/PolicyService.sol @@ -205,7 +205,7 @@ contract PolicyService is } // check if premium has already been collected - if (instanceReader.getPremiumInfoState(policyNftId) == PAID()) { + if (instanceReader.getPremiumState(policyNftId) == PAID()) { revert ErrorPolicyServicePremiumAlreadyPaid(policyNftId); } @@ -395,7 +395,7 @@ contract PolicyService is RiskId riskId = policyInfo.riskId; NftId bundleNftId = policyInfo.bundleNftId; - if (instanceReader.getPremiumInfoState(policyNftId) != PAID()) { + if (instanceReader.getPremiumState(policyNftId) != PAID()) { revert ErrorPolicyServicePremiumNotPaid(policyNftId, policyInfo.premiumAmount); } diff --git a/contracts/registry/RegistryAdmin.sol b/contracts/registry/RegistryAdmin.sol index b19c7b85b..ed0dba082 100644 --- a/contracts/registry/RegistryAdmin.sol +++ b/contracts/registry/RegistryAdmin.sol @@ -146,7 +146,7 @@ contract RegistryAdmin is RoleInfo memory roleInfo = authorization.getRoleInfo(roleId); // create role if not exists - if (!roleExists(roleInfo.name)) { + if (!roleExists(roleInfo.name.toString())) { _createRole( roleId, roleInfo); diff --git a/contracts/shared/ComponentService.sol b/contracts/shared/ComponentService.sol index d6a10f499..5e31f8348 100644 --- a/contracts/shared/ComponentService.sol +++ b/contracts/shared/ComponentService.sol @@ -79,7 +79,7 @@ contract ComponentService is } // check provided address is product contract - if (!ContractLib.isInstanceLinkedComponent(address(registry), componentAddress)) { + if (!_isInstanceLinkedComponent(componentAddress)) { revert ErrorComponentServiceNotComponent(componentAddress); } @@ -214,7 +214,7 @@ contract ComponentService is } // check provided address is product contract - if (!ContractLib.isProduct(address(registry), productAddress)) { + if (!_isProduct(productAddress)) { revert ErrorComponentServiceNotProduct(productAddress); } @@ -227,6 +227,24 @@ contract ComponentService is } + function _isProduct(address target) internal view virtual returns (bool) { + if (!_isInstanceLinkedComponent(target)) { + return false; + } + + return IInstanceLinkedComponent(target).getInitialInfo().objectType == PRODUCT(); + } + + + function _isInstanceLinkedComponent(address target) internal view virtual returns (bool) { + if (!ContractLib.isContract(target)) { + return false; + } + + return ContractLib.supportsInterface(target, type(IInstanceLinkedComponent).interfaceId); + } + + /// @inheritdoc IComponentService function setProductFees( Fee memory productFee, // product fee on net premium @@ -265,8 +283,7 @@ contract ComponentService is function _createProduct( InstanceStore instanceStore, NftId productNftId, - address productAddress, - address token + address productAddress ) internal virtual @@ -479,7 +496,7 @@ contract ComponentService is objectInfo.initialOwner).nftId; // create product info in instance store - _createProduct(instanceStore, componentNftId, componentAddress, token); + _createProduct(instanceStore, componentNftId, componentAddress); } else { // register non product component with registry componentNftId = _registryService.registerProductLinkedComponent( diff --git a/contracts/shared/ContractLib.sol b/contracts/shared/ContractLib.sol index fec76ff0e..69eea4353 100644 --- a/contracts/shared/ContractLib.sol +++ b/contracts/shared/ContractLib.sol @@ -5,13 +5,12 @@ import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165C import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol"; import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; -import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol"; import {IPolicyHolder} from "../shared/IPolicyHolder.sol"; import {IRegistry} from "../registry/IRegistry.sol"; import {IService} from "../shared/IService.sol"; import {NftId} from "../type/NftId.sol"; -import {ObjectType, INSTANCE, PRODUCT, DISTRIBUTION, ORACLE, POOL, STAKING} from "../type/ObjectType.sol"; +import {ObjectType, INSTANCE, PRODUCT, DISTRIBUTION, ORACLE, POOL} from "../type/ObjectType.sol"; import {VersionPart} from "../type/Version.sol"; interface ITargetHelper { @@ -99,25 +98,6 @@ library ContractLib { } - // TODO cleanup - // function getAndVerifyStaking( - // IRegistry registry, - // address target - // ) - // external - // view - // returns (IRegistry.ObjectInfo memory info) - // { - // // check target is component - // info = _getAndVerifyObjectInfo(registry, target); - // if(info.objectType != STAKING()) { - // revert ErrorContractLibNotStaking( - // info.nftId, - // info.objectType); - // } - // } - - function getInstanceForComponent( IRegistry registry, NftId componentNftId @@ -179,32 +159,6 @@ library ContractLib { } - function isProduct(address registry, address target) - public - view - returns (bool) - { - if (!isInstanceLinkedComponent(registry, target)) { - return false; - } - - return IInstanceLinkedComponent(target).getInitialInfo().objectType == PRODUCT(); - } - - - function isInstanceLinkedComponent(address registry, address target) - public - view - returns (bool) - { - if (!isContract(target)) { - return false; - } - - return supportsInterface(target, type(IInstanceLinkedComponent).interfaceId); - } - - function isRegistered(address registry, address caller, ObjectType expectedType) public view returns (bool) { NftId nftId = IRegistry(registry).getNftIdForAddress(caller); if (nftId.eqz()) { diff --git a/contracts/shared/IRegisterable.sol b/contracts/shared/IRegisterable.sol index 18a04418e..071408a33 100644 --- a/contracts/shared/IRegisterable.sol +++ b/contracts/shared/IRegisterable.sol @@ -6,7 +6,6 @@ import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessMana import {INftOwnable} from "./INftOwnable.sol"; import {IRelease} from "../registry/IRelease.sol"; import {IRegistry} from "../registry/IRegistry.sol"; -import {VersionPart} from "../type/Version.sol"; /// @title IRegisterable /// @dev Marks contracts that are intended to be registered in the registry. diff --git a/contracts/staking/Staking.sol b/contracts/staking/Staking.sol index 8fde464ec..24afb13e1 100644 --- a/contracts/staking/Staking.sol +++ b/contracts/staking/Staking.sol @@ -243,7 +243,7 @@ contract Staking is returns (Amount newBalance) { StakingStorage storage $ = _getStakingStorage(); - uint chainId = $._reader.getTargetInfo(targetNftId).chainId; + uint256 chainId = $._reader.getTargetInfo(targetNftId).chainId; UFixed stakingRate = $._reader.getStakingRate(chainId, token); newBalance = $._store.increaseTotalValueLocked(targetNftId, stakingRate, token, amount); } @@ -256,7 +256,7 @@ contract Staking is returns (Amount newBalance) { StakingStorage storage $ = _getStakingStorage(); - uint chainId = $._reader.getTargetInfo(targetNftId).chainId; + uint256 chainId = $._reader.getTargetInfo(targetNftId).chainId; UFixed stakingRate = $._reader.getStakingRate(chainId, token); newBalance = $._store.decreaseTotalValueLocked(targetNftId, stakingRate, token, amount); } @@ -495,8 +495,9 @@ contract Staking is } + /// @dev top level initializer (upgradable contract) function _initialize( - address owner, + address, bytes memory data ) internal @@ -513,8 +514,6 @@ contract Staking is // only admin(authority) and dip token address are set in registry at this point IRegistry registry = IRegistry(registryAddress); address authority = registry.getAuthority(); - TokenRegistry tokenRegistry = TokenRegistry(tokenRegistryAddress); - address dipTokenAddress = tokenRegistry.getDipTokenAddress(); // wiring to external contracts StakingStorage storage $ = _getStakingStorage(); diff --git a/contracts/type/ObjectType.sol b/contracts/type/ObjectType.sol index a2537a719..da7f6d213 100644 --- a/contracts/type/ObjectType.sol +++ b/contracts/type/ObjectType.sol @@ -290,7 +290,7 @@ library ObjectTypeLib { } bytes memory buffer = new bytes(digits); - uint index = digits - 1; + uint256 index = digits - 1; temp = value; while (temp != 0) { diff --git a/contracts/type/String.sol b/contracts/type/String.sol index 6cb7a4048..4191d7298 100644 --- a/contracts/type/String.sol +++ b/contracts/type/String.sol @@ -12,10 +12,12 @@ using { StrLib.length } for Str global; +// pure free function needed for the operator overloading function StrEq(Str s1, Str s2) pure returns (bool) { return StrLib.eq(s1, s2); } +// pure free function needed for the operator overloading function StrNe(Str s1, Str s2) pure returns (bool) { return StrLib.ne(s1, s2); } @@ -39,6 +41,16 @@ library StrLib { return Str.unwrap(s1) != Str.unwrap(s2); } + /// @dev return true iff s1 equals from s2 + function eq(string memory s1, string memory s2) public pure returns (bool) { + return keccak256(bytes(s1)) == keccak256(bytes(s2)); + } + + /// @dev return true iff s1 differs s2 + function ne(string memory s1, string memory s2) public pure returns (bool) { + return !eq(s1, s2); + } + /// @dev converts the provided short string into a string. /// uses ShortStrings.toString function toString(Str str) public pure returns (string memory) { diff --git a/contracts/upgradeability/ProxyManager.sol b/contracts/upgradeability/ProxyManager.sol index bd363f836..b14ba1adf 100644 --- a/contracts/upgradeability/ProxyManager.sol +++ b/contracts/upgradeability/ProxyManager.sol @@ -39,8 +39,8 @@ contract ProxyManager is UpgradableProxyWithAdmin internal _proxy; // state to keep version history - mapping(Version version => VersionInfo info) _versionHistory; - Version [] _versions; + mapping(Version version => VersionInfo info) internal _versionHistory; + Version [] internal _versions; /// @dev convencience initializer function initialize( diff --git a/test/TestProduct.t.sol b/test/TestProduct.t.sol index c033f6f6c..41391f468 100644 --- a/test/TestProduct.t.sol +++ b/test/TestProduct.t.sol @@ -213,7 +213,7 @@ contract TestProduct is GifTest { assertTrue(policyInfo.expiredAt.gtz(), "expiredAt not set"); assertTrue(policyInfo.expiredAt.toInt() == policyInfo.activatedAt.addSeconds(sec30).toInt(), "expiredAt not activatedAt + 30"); - assertEq(instanceReader.getPremiumInfoState(policyNftId).toInt(), EXPECTED().toInt(), "premium info state not CALCULATED"); + assertEq(instanceReader.getPremiumState(policyNftId).toInt(), EXPECTED().toInt(), "premium info state not CALCULATED"); // solhint-disable-next-line console.log("checking bundle amounts after underwriting"); @@ -318,7 +318,7 @@ contract TestProduct is GifTest { assertTrue(policyInfo.expiredAt.gtz(), "expiredAt not set"); assertTrue(policyInfo.expiredAt.toInt() == policyInfo.activatedAt.addSeconds(sec30).toInt(), "expiredAt not activatedAt + 30"); - assertEq(instanceReader.getPremiumInfoState(policyNftId).toInt(), PAID().toInt(), "premium info state not CALCULATED"); + assertEq(instanceReader.getPremiumState(policyNftId).toInt(), PAID().toInt(), "premium info state not CALCULATED"); // solhint-disable-next-line console.log("checking token balances after underwriting"); @@ -654,7 +654,7 @@ contract TestProduct is GifTest { IPolicy.PolicyInfo memory policyInfo = instanceReader.getPolicyInfo(policyNftId); assertEq(policyInfo.premiumAmount.toInt(), 137, "unexpected premium amount from application"); - assertEq(instanceReader.getPremiumInfoState(policyNftId).toInt(), PAID().toInt(), "unexpected premium info state"); + assertEq(instanceReader.getPremiumState(policyNftId).toInt(), PAID().toInt(), "unexpected premium info state"); } diff --git a/test/authorization/AccessAdmin.t.sol b/test/authorization/AccessAdmin.t.sol index ae0a095d0..c49fbea1c 100644 --- a/test/authorization/AccessAdmin.t.sol +++ b/test/authorization/AccessAdmin.t.sol @@ -103,6 +103,20 @@ contract AccessAdminForTesting is AccessAdmin { external restricted() { + // check role does not yet exist + if (roleExists(roleId)) { + revert IAccessAdmin.ErrorAccessAdminRoleAlreadyCreated( + roleId, + getRoleInfo(roleId).name.toString()); + } + + if (roleExists(name)) { + revert IAccessAdmin.ErrorAccessAdminRoleNameAlreadyExists( + roleId, + name, + getRoleForName(name)); + } + _createRole( roleId, AccessAdminLib.toRole( @@ -173,9 +187,6 @@ contract AccessAdminForTesting is AccessAdmin { virtual restricted() { - bool createRole = true; - bool customTarget = true; - bool checkAuthoritiy = true; _createManagedTarget(target, name, IAccess.TargetType.Custom); } @@ -232,7 +243,7 @@ contract AccessAdminBaseTest is Test { RegistryAdmin public registryAdmin; Registry public registry; - VersionPart release; + VersionPart public release; AccessManagerCloneable public accessManager; AccessAdminForTesting public accessAdmin; @@ -249,7 +260,7 @@ contract AccessAdminBaseTest is Test { // create registry and release version registryAdmin = new RegistryAdmin(); registry = new Registry(registryAdmin, globalRegistry); - VersionPart release = VersionPartLib.toVersionPart(3); + release = VersionPartLib.toVersionPart(3); // complete setup (which links internal acccess manager to registry and release) // and grants manager role to deployer @@ -578,7 +589,7 @@ contract AccessAdminTest is AccessAdminBaseTest { newRoleName); vm.stopPrank(); - // WHEN + THEN - use existing role id + // WHEN + THEN - use existing role id with differnt name vm.expectRevert( abi.encodeWithSelector( IAccessAdmin.ErrorAccessAdminRoleAlreadyCreated.selector, @@ -592,7 +603,7 @@ contract AccessAdminTest is AccessAdminBaseTest { "SomeOtherRule"); vm.stopPrank(); - // WHEN + THEN - use existing role name + // WHEN + THEN - use existing role name with different role id RoleId otherRoleId = RoleIdLib.toRoleId(123); vm.expectRevert( abi.encodeWithSelector( @@ -851,7 +862,6 @@ contract AccessAdminTest is AccessAdminBaseTest { function test_accessAdminGrantRevokeNonexistentRole() public { // GIVEN - setup - RoleId adminRoleId = accessAdmin.getAdminRole(); RoleId missingRoleId = RoleIdLib.toRoleId(404); // WHEN + THEN grant/revoke @@ -901,8 +911,7 @@ contract AccessAdminTest is AccessAdminBaseTest { console.log("attempt to grant admin role"); vm.expectRevert( abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminRoleIsLocked.selector, - adminRole)); + IAccessAdmin.ErrorAccessAdminInvalidUserOfAdminRole.selector)); accessAdmin.grantRole(outsider, adminRole); @@ -910,8 +919,7 @@ contract AccessAdminTest is AccessAdminBaseTest { console.log("attempt to revoke admin role"); vm.expectRevert( abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminRoleIsLocked.selector, - adminRole)); + IAccessAdmin.ErrorAccessAdminInvalidUserOfAdminRole.selector)); accessAdmin.revokeRole(outsider, adminRole); @@ -919,8 +927,7 @@ contract AccessAdminTest is AccessAdminBaseTest { console.log("attempt to renounce admin role"); vm.expectRevert( abi.encodeWithSelector( - IAccessAdmin.ErrorAccessAdminRoleIsLocked.selector, - adminRole)); + IAccessAdmin.ErrorAccessAdminInvalidUserOfAdminRole.selector)); accessAdmin.renounceRole(adminRole); @@ -931,8 +938,7 @@ contract AccessAdminTest is AccessAdminBaseTest { console.log("attempt to grant public role"); vm.expectRevert( abi.encodeWithSelector( - IAccessManager.AccessManagerLockedRole.selector, - publicRole)); + IAccessAdmin.ErrorAccessAdminInvalidUserOfPublicRole.selector)); accessAdmin.grantRole(outsider, publicRole); @@ -940,8 +946,7 @@ contract AccessAdminTest is AccessAdminBaseTest { console.log("attempt to revoke public role"); vm.expectRevert( abi.encodeWithSelector( - IAccessManager.AccessManagerLockedRole.selector, - publicRole)); + IAccessAdmin.ErrorAccessAdminInvalidUserOfPublicRole.selector)); accessAdmin.revokeRole(outsider, publicRole); @@ -949,8 +954,7 @@ contract AccessAdminTest is AccessAdminBaseTest { console.log("attempt to renounce public role"); vm.expectRevert( abi.encodeWithSelector( - IAccessManager.AccessManagerLockedRole.selector, - publicRole)); + IAccessAdmin.ErrorAccessAdminInvalidUserOfPublicRole.selector)); accessAdmin.renounceRole(publicRole); diff --git a/test/examples/fire/FireProduct.t.sol b/test/examples/fire/FireProduct.t.sol index 109dbe1fd..e4edd27ee 100644 --- a/test/examples/fire/FireProduct.t.sol +++ b/test/examples/fire/FireProduct.t.sol @@ -117,7 +117,7 @@ contract FireProductTest is FireTestBase { assertEq(TimestampLib.zero(), policy.closedAt, "policy.closedAt mismatch"); // check premium state is PAID (product uses immediate payment) and then check the premium values - assertTrue(PAID().eq(instanceReader.getPremiumInfoState(policyNftId))); + assertTrue(PAID().eq(instanceReader.getPremiumState(policyNftId))); IPolicy.PremiumInfo memory premiumInfo = instanceReader.getPremiumInfo(policyNftId); assertEq((5000 + 100) * 10 ** 6, premiumInfo.fullPremiumAmount.toInt()); assertEq(premium, premiumInfo.fullPremiumAmount, "premiumInfo.fullPremiumAmount mismatch"); diff --git a/test/instance/authorization/InstanceAuthzTargets.t.sol b/test/instance/authorization/InstanceAuthzTargets.t.sol index b66647921..3d4dc7e68 100644 --- a/test/instance/authorization/InstanceAuthzTargets.t.sol +++ b/test/instance/authorization/InstanceAuthzTargets.t.sol @@ -8,11 +8,9 @@ import {IAccess} from "../../../contracts/authorization/IAccess.sol"; import {IAccessAdmin} from "../../../contracts/authorization/IAccessAdmin.sol"; import {IInstance} from "../../../contracts/instance/IInstance.sol"; -import {AccessAdminLib} from "../../../contracts/authorization/AccessAdminLib.sol"; import {AccessManagedMock} from "../../mock/AccessManagedMock.sol"; import {InstanceAuthzBaseTest} from "./InstanceAuthzBase.t.sol"; -import {INftOwnable} from "../../../contracts/shared/INftOwnable.sol"; -import {RoleId, RoleIdLib, ADMIN_ROLE, INSTANCE_OWNER_ROLE} from "../../../contracts/type/RoleId.sol"; +import {RoleId, RoleIdLib} from "../../../contracts/type/RoleId.sol"; import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; @@ -84,7 +82,7 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { string memory targetName = "MyTarget"; vm.prank(instanceOwner); - RoleId myTargetRoleId = instance.createTarget(address(target), targetName); + instance.createTarget(address(target), targetName); assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); assertFalse(instanceReader.isLocked(address(target)), "target locked"); @@ -214,11 +212,12 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { //--- helper functions ----------------------------------------------------// - function _deployAccessManagedMock() internal returns (AccessManagedMock accessManagedMock) { - return _deployAccessManagedMock(instance.authority()); - } + // TODO cleanup + // function _deployAccessManagedMock() internal returns (AccessManagedMock accessManagedMock) { + // return _deployAccessManagedMock(); + // } - function _deployAccessManagedMock(address authority) internal returns (AccessManagedMock accessManagedMock) { + function _deployAccessManagedMock() internal returns (AccessManagedMock accessManagedMock) { accessManagedMock = new AccessManagedMock(instance.authority()); } } \ No newline at end of file From 9294f0957517a2fa5d4d7a42f50e65ea65a03137 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Tue, 3 Sep 2024 18:13:37 +0000 Subject: [PATCH 17/18] amend hh deploy script --- scripts/libs/instance.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/libs/instance.ts b/scripts/libs/instance.ts index a26f6a8b2..68b897b96 100644 --- a/scripts/libs/instance.ts +++ b/scripts/libs/instance.ts @@ -151,6 +151,7 @@ export async function deployAndRegisterMasterInstance( [], { libraries: { + AccessAdminLib: libraries.accessAdminLibAddress, AmountLib: libraries.amountLibAddress, ClaimIdLib: libraries.claimIdLibAddress, DistributorTypeLib: libraries.distributorTypeLibAddress, From a28dfa009f9afcc69ead686d0030735b71975ef8 Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Tue, 3 Sep 2024 18:38:36 +0000 Subject: [PATCH 18/18] amend function (un)authorization tests --- .../authorization/InstanceAuthzTargets.t.sol | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/test/instance/authorization/InstanceAuthzTargets.t.sol b/test/instance/authorization/InstanceAuthzTargets.t.sol index 3d4dc7e68..6a7c05cb0 100644 --- a/test/instance/authorization/InstanceAuthzTargets.t.sol +++ b/test/instance/authorization/InstanceAuthzTargets.t.sol @@ -11,6 +11,7 @@ import {IInstance} from "../../../contracts/instance/IInstance.sol"; import {AccessManagedMock} from "../../mock/AccessManagedMock.sol"; import {InstanceAuthzBaseTest} from "./InstanceAuthzBase.t.sol"; import {RoleId, RoleIdLib} from "../../../contracts/type/RoleId.sol"; +import {StrLib} from "../../../contracts/type/String.sol"; import {TimestampLib} from "../../../contracts/type/Timestamp.sol"; @@ -109,7 +110,7 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { assertTrue(instanceReader.isLocked(address(target)), "target not locked after locking instance"); } - function test_instanceAuthzTargetsAuthorizeFunctionsHappyCase() public { + function test_instanceAuthzFunctionsAuthorizeFunctionsHappyCase() public { // GIVEN AccessManagedMock target = _deployAccessManagedMock(); string memory targetName = "MyTarget"; @@ -119,6 +120,7 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { assertTrue(instanceReader.targetExists(address(target)), "target not existing after create"); assertFalse(instanceReader.isLocked(address(target)), "target locked"); + assertEq(instanceReader.authorizedFunctions(address(target)), 0, "unexpected number of authorized functions (before)"); assertEq(target.counter1(), 0, "unexpected initial counter value"); // WHEN + THEN attempt to call unauthorized function @@ -134,28 +136,44 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { // WHEN - add function authz RoleId publicRoleId = instance.getInstanceAdmin().getPublicRole(); - IAccess.FunctionInfo[] memory functions = new IAccess.FunctionInfo[](1); + IAccess.FunctionInfo[] memory functions = new IAccess.FunctionInfo[](2); functions[0] = instanceReader.toFunction(AccessManagedMock.increaseCounter1.selector, "increaseCounter1"); + functions[1] = instanceReader.toFunction(AccessManagedMock.increaseCounter2.selector, "increaseCounter2"); vm.prank(instanceOwner); instance.authorizeFunctions(address(target), publicRoleId, functions); + // THEN + assertEq(instanceReader.authorizedFunctions(address(target)), 2, "unexpected number of authorized functions (after)"); + + (IAccess.FunctionInfo memory func, RoleId authorizedRole) = instanceReader.getAuthorizedFunction(address(target), 0); + assertTrue(StrLib.eq(func.name.toString(), "increaseCounter1"), "unexpected function name"); + assertEq(func.selector.toBytes4(), AccessManagedMock.increaseCounter1.selector, "unexpected function selector"); + assertEq(func.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected function creation time"); + assertEq(authorizedRole.toInt(), publicRoleId.toInt(), "unexpected authorized role"); + + (func, authorizedRole) = instanceReader.getAuthorizedFunction(address(target), 1); + assertTrue(StrLib.eq(func.name.toString(), "increaseCounter2"), "unexpected function name"); + assertEq(func.selector.toBytes4(), AccessManagedMock.increaseCounter2.selector, "unexpected function selector"); + assertEq(func.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected function creation time"); + assertEq(authorizedRole.toInt(), publicRoleId.toInt(), "unexpected authorized role"); + // must not revert now vm.prank(outsider); target.increaseCounter1(); - // THEN assertEq(target.counter1(), 1, "unexpected counter value after increaseCounter1"); } - function test_instanceAuthzTargetsUnauthorizeFunctionsHappyCase() public { + function test_instanceAuthzFunctionsUnauthorizeFunctionsHappyCase() public { // GIVEN AccessManagedMock target = _deployAccessManagedMock(); string memory targetName = "MyTarget"; RoleId publicRoleId = instance.getInstanceAdmin().getPublicRole(); - IAccess.FunctionInfo[] memory functions = new IAccess.FunctionInfo[](1); + IAccess.FunctionInfo[] memory functions = new IAccess.FunctionInfo[](2); functions[0] = instanceReader.toFunction(AccessManagedMock.increaseCounter1.selector, "increaseCounter1"); + functions[1] = instanceReader.toFunction(AccessManagedMock.increaseCounter2.selector, "increaseCounter2"); vm.startPrank(instanceOwner); instance.createTarget(address(target), targetName); @@ -166,13 +184,24 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { vm.prank(outsider); target.increaseCounter1(); - assertEq(target.counter1(), 1, "unexpected counter value after increaseCounter1"); + assertEq(instanceReader.authorizedFunctions(address(target)), 2, "unexpected number of authorized functions (before)"); + + // WHEN - unauthorize 1st function + functions = new IAccess.FunctionInfo[](1); + functions[0] = instanceReader.toFunction(AccessManagedMock.increaseCounter1.selector, "increaseCounter1"); - // WHEN - unauthorize function vm.prank(instanceOwner); instance.unauthorizeFunctions(address(target), functions); // THEN + assertEq(instanceReader.authorizedFunctions(address(target)), 1, "unexpected number of authorized functions (after)"); + + (IAccess.FunctionInfo memory func, RoleId authorizedRole) = instanceReader.getAuthorizedFunction(address(target), 0); + assertTrue(StrLib.eq(func.name.toString(), "increaseCounter2"), "unexpected function name"); + assertEq(func.selector.toBytes4(), AccessManagedMock.increaseCounter2.selector, "unexpected function selector"); + assertEq(func.createdAt.toInt(), TimestampLib.blockTimestamp().toInt(), "unexpected function creation time"); + assertEq(authorizedRole.toInt(), publicRoleId.toInt(), "unexpected authorized role"); + vm.expectRevert( abi.encodeWithSelector( IAccessManaged.AccessManagedUnauthorized.selector, @@ -185,7 +214,7 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { } - function test_instanceAuthzToTargetAll() public { + function test_instanceAuthzFunctionsToFunction() public { bytes4 signature = AccessManagedMock.increaseCounter1.selector; string memory name = "increaseCounter1"; @@ -212,11 +241,6 @@ contract InstanceAuthzTargetsTest is InstanceAuthzBaseTest { //--- helper functions ----------------------------------------------------// - // TODO cleanup - // function _deployAccessManagedMock() internal returns (AccessManagedMock accessManagedMock) { - // return _deployAccessManagedMock(); - // } - function _deployAccessManagedMock() internal returns (AccessManagedMock accessManagedMock) { accessManagedMock = new AccessManagedMock(instance.authority()); }