-
Notifications
You must be signed in to change notification settings - Fork 18
feat: composite policies (UNION/INTERSECT) — interface, mock, and tests #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1a391f4
e685897
0c29a7e
5c14492
0f599ad
57e6caa
2594d62
6f30915
bc683b1
35e816a
fe6710a
0682ec3
050a4b0
02e8f22
4d27af7
2bd24c2
03230f2
500e269
14c9bb5
aa0dc53
ffc3105
fb594b2
09a8792
f6f2825
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ pragma solidity >=0.8.20 <0.9.0; | |
|
|
||
| /// @title IPolicyRegistry | ||
| /// | ||
| /// @notice Singleton registry of address-membership policies. Policies are referenced by | ||
| /// @notice Singleton registry of simple and composite policies. Policies are referenced by | ||
| /// `uint64 policyId` and queried via `isAuthorized(policyId, account)`. | ||
| interface IPolicyRegistry { | ||
| /*////////////////////////////////////////////////////////////// | ||
|
|
@@ -12,11 +12,19 @@ interface IPolicyRegistry { | |
|
|
||
| /// @notice Policy type discriminator. | ||
| /// | ||
| /// @param BLOCKLIST Authorized unless in the policy's set. | ||
| /// @param ALLOWLIST Authorized only if in the policy's set. | ||
| /// @dev Two kinds of policy: | ||
| /// - Simple (BLOCKLIST, ALLOWLIST): decide from an address set. | ||
| /// - Composite (UNION, INTERSECT): decide by combining child simple policies. | ||
| /// | ||
| /// @param BLOCKLIST Account is authorized unless it is in the set. | ||
| /// @param ALLOWLIST Account is authorized only if it is in the set. | ||
| /// @param UNION (OR). Account is authorized if any child policy authorizes it. | ||
| /// @param INTERSECT (AND). Account is authorized only if every child policy authorizes it. | ||
| enum PolicyType { | ||
| BLOCKLIST, | ||
| ALLOWLIST | ||
| ALLOWLIST, | ||
| UNION, | ||
| INTERSECT | ||
| } | ||
|
|
||
| /*////////////////////////////////////////////////////////////// | ||
|
|
@@ -48,6 +56,18 @@ interface IPolicyRegistry { | |
| /// @notice `finalizeUpdateAdmin` was called with no pending admin staged. | ||
| error NoPendingAdmin(); | ||
|
|
||
| /// @notice A composite policy was created or updated with a child-policy count outside the | ||
| /// permitted range. A composite must reference between `min` and `max` simple policies, | ||
| /// inclusive. | ||
| /// @param min Minimum number of child policies permitted. | ||
| /// @param max Maximum number of child policies permitted. | ||
| error ChildPoliciesOutsideOfRange(uint256 min, uint256 max); | ||
|
|
||
| /// @notice Composite policies are not simple policies. Child policies must be existing | ||
| /// ALLOWLIST or BLOCKLIST policies; | ||
| /// @param childPolicyId The offending child policy ID. | ||
| error InvalidChildPolicy(uint64 childPolicyId); | ||
|
|
||
| /*////////////////////////////////////////////////////////////// | ||
| EVENTS | ||
| //////////////////////////////////////////////////////////////*/ | ||
|
|
@@ -68,24 +88,29 @@ interface IPolicyRegistry { | |
| /// @notice One or more accounts had their BLOCKLIST membership set to `blocked` in a single batch. | ||
| event BlocklistUpdated(uint64 indexed policyId, address indexed updater, bool blocked, address[] accounts); | ||
|
|
||
| /// @notice A composite policy's child set was set or replaced in full with `childPolicyIds`. Emitted | ||
| /// on composite creation and on every subsequent update; carries the complete post-update set. | ||
| event CompositePolicyUpdated(uint64 indexed policyId, address indexed updater, uint64[] childPolicyIds); | ||
|
|
||
| /*////////////////////////////////////////////////////////////// | ||
| POLICY CREATION | ||
| //////////////////////////////////////////////////////////////*/ | ||
|
|
||
| /// @notice Creates a new policy with no initial members. Permissionless. | ||
| /// @notice Creates a new simple policy with no initial members. Permissionless. | ||
| /// | ||
| /// @dev Reverts with `ZeroAddress` when `admin` is `address(0)`. | ||
| /// @dev Panics with arithmetic overflow (Panic 0x11) when the policy counter has reached its maximum value. | ||
| /// @dev Reverts with `IncompatiblePolicyType` when `policyType` is a composite gate. | ||
| /// | ||
| /// @param admin Initial admin authorized to modify membership and transfer or renounce administration. | ||
| /// @param policyType BLOCKLIST or ALLOWLIST. | ||
| /// | ||
| /// @return newPolicyId The newly assigned policy ID. | ||
| function createPolicy(address admin, PolicyType policyType) external returns (uint64 newPolicyId); | ||
|
|
||
| /// @notice Creates a new policy seeded with `accounts` as initial members. Permissionless. | ||
| /// @notice Creates a new simple policy seeded with `accounts` as initial members. Permissionless. | ||
| /// | ||
| /// @dev Reverts with `ZeroAddress` when `admin` is `address(0)`. Takes precedence over `BatchSizeTooLarge`. | ||
| /// @dev Reverts with `IncompatiblePolicyType` when `policyType` is a composite policyType. | ||
| /// @dev Reverts with `BatchSizeTooLarge` when `accounts.length` exceeds the registry limit. | ||
| /// @dev Panics with arithmetic overflow (Panic 0x11) when the policy counter has reached its maximum value. | ||
| /// | ||
|
|
@@ -98,6 +123,28 @@ interface IPolicyRegistry { | |
| external | ||
| returns (uint64 newPolicyId); | ||
|
|
||
| /// @notice Creates a new composite policy that combines existing simple policies under a logic | ||
| /// gate. | ||
| /// | ||
| /// @dev Child policies must be simple policies (ALLOWLIST or BLOCKLIST), never another composite. | ||
|
rayyan224 marked this conversation as resolved.
|
||
| /// The child-policy set is capped at 4. | ||
| /// @dev Reverts with `IncompatiblePolicyType` when `policyType` is not UNION or INTERSECT. | ||
| /// @dev Reverts with `ZeroAddress` when `admin` is `address(0)`. | ||
| /// @dev Reverts with `ChildPoliciesOutsideOfRange(2, 4)` when `childPolicyIds.length` is not in `[2, 4]`. | ||
| /// @dev Reverts with `PolicyNotFound` when any child policy does not exist. | ||
| /// @dev Reverts with `InvalidChildPolicy` when any child policy is not a simple policy or a built-in policy. | ||
| /// @dev Panics with arithmetic overflow (Panic 0x11) when the policy counter has reached its maximum value. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aren't we going to explicitly check that
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They share the create counter which can overflow
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oooh for the global policy id. i don't think this needs explicit callout here... |
||
| /// | ||
| /// @param admin Initial admin authorized to update child policies and transfer or renounce | ||
| /// administration. | ||
| /// @param policyType UNION or INTERSECT. | ||
| /// @param childPolicyIds Existing simple policy IDs to combine. | ||
| /// | ||
| /// @return newPolicyId The newly assigned composite policy ID. | ||
| function createCompositePolicy(address admin, PolicyType policyType, uint64[] calldata childPolicyIds) | ||
| external | ||
| returns (uint64 newPolicyId); | ||
|
|
||
| /*////////////////////////////////////////////////////////////// | ||
| POLICY ADMINISTRATION | ||
| //////////////////////////////////////////////////////////////*/ | ||
|
|
@@ -154,6 +201,21 @@ interface IPolicyRegistry { | |
| /// @param accounts Accounts to update. | ||
| function updateBlocklist(uint64 policyId, bool blocked, address[] calldata accounts) external; | ||
|
|
||
| /// @notice Replaces a composite policy's child-policy set in full with `childPolicyIds`. | ||
| /// @dev Reverts with `PolicyNotFound` when `policyId` does not exist. | ||
| /// @dev Reverts with `IncompatiblePolicyType` when `policyId` is not a composite (UNION or INTERSECT). | ||
| /// @dev Reverts with `Unauthorized` when the caller is not the current admin. A renounced composite | ||
| /// (admin `address(0)`) can never be updated. | ||
| /// @dev Reverts with `ChildPoliciesOutsideOfRange(2, 4)` when `childPolicyIds.length` is not in `[2, 4]`; | ||
| /// there is no clear-the-list path (the composite child-policy range, not the 64-account batch limit). | ||
| /// @dev Reverts with `PolicyNotFound` when any child policy does not exist. | ||
| /// @dev Reverts with `InvalidChildPolicy` when any child policy is itself a composite | ||
| /// (not a simple policy). | ||
|
ilikesymmetry marked this conversation as resolved.
|
||
| /// | ||
| /// @param policyId Composite policy to update. | ||
| /// @param childPolicyIds Complete new set of existing simple policy IDs. | ||
| function updateComposite(uint64 policyId, uint64[] calldata childPolicyIds) external; | ||
|
|
||
| /*////////////////////////////////////////////////////////////// | ||
| AUTHORIZATION QUERIES | ||
| //////////////////////////////////////////////////////////////*/ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createPolicyandcreatePolicyWithAccountshave natspec mentioning that they revert for composite policy types, but it looks likedon't revert as documented
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yep nice catch