refactor(access): harden the shieldedaccesscontrol lib by some improvements and fixing some bugs#382
refactor(access): harden the shieldedaccesscontrol lib by some improvements and fixing some bugs#3820xisk wants to merge 2 commits intoshielded-access-controlfrom
Conversation
…nd fixing some bugs
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Signed-off-by: 0xisk <iskander.andrews@openzeppelin.com>
| ShieldedAccessControl__roleCommitmentNullifiers, | ||
| ShieldedAccessControl__adminRoles }; |
There was a problem hiding this comment.
The test file depends the re-export of adminRoles
| * @param {Bytes<32>} role - The role identifier. | ||
| * @param {ZswapCoinPublicKey} account - The public key of the account to check. | ||
| * | ||
| * @return {Boolean} - A boolean determining if `account` holds `role`. |
There was a problem hiding this comment.
| * @return {Boolean} - A boolean determining if `account` holds `role`. | |
| * @return {Boolean} - A boolean determining if a caller successfully proved ownership of `role` |
| } | ||
|
|
||
| /** | ||
| * @description Returns `true` if `account` holds `role` and is not revoked. The account identifier |
There was a problem hiding this comment.
| * @description Returns `true` if `account` holds `role` and is not revoked. The account identifier | |
| * @description Returns `true` if a caller proves ownership of `role` and is not revoked.The account identifier |
| export circuit hasRole(role: Bytes<32>, account: ZswapCoinPublicKey): Boolean { | ||
| Initializable_assertInitialized(); | ||
|
|
||
| return _hasRole(role, _computeAccountId(role, account)); | ||
| } |
There was a problem hiding this comment.
Any circuit with the public key as part of its circuit signature should not be part of the public API since a consumer could accidentally dox themselves by calling it.
| export circuit computeAccountId(role: Bytes<32>, account: ZswapCoinPublicKey): Bytes<32> { | ||
| Initializable_assertInitialized(); | ||
|
|
||
| return _computeAccountId(role, account); | ||
| } |
There was a problem hiding this comment.
Same issue here, anyone calling this circuit would dox themselves and it should not be part of the public API
There was a problem hiding this comment.
In general, I'm not sold on getting rid of the nominally typed aliases. I think forcing consumers to pass Bytes<32> values as RoleIdentifier, AccountId etc makes interacting with the contract more safe which is one of our top concerns. This is especially important given that Compact doesn't have an official LSP server implementation making circuit parameter hinting impossible and super easy to mess up param inputs
There was a problem hiding this comment.
In my opinion it's ok to deviate from the Solidity naming scheme in this case since the new method names are a better semantic fit for what's actually happening. hasRole implies we're performing a simple database lookup but that's not what's happening under the hood. The caller is proving they have knowledge of a Merkle path to a specific commitment in the public Merkle tree. Even if the caller has the role the circuit can still fail with bad witness values. There's a certain degree of absolutism to hasRole. The value either exists or it doesnt and there are no failure cases in between which doesnt fit in this case.
I dont think _hasRole is a good rename for _validateRole for the same reasons described above
There was a problem hiding this comment.
assertOnlyRole is meant to be part of the public API and should not be changed to an internal method.
There was a problem hiding this comment.
I like the idea of an _uncheckedXXX to reduce the Initialization checks in the call chain. After thinking about it some more we can get rid of the Initialization checks in the unexported internal only circuits like _computeAccountId _validateRole etc. However I have some concerns about this: on one hand, they're internal only and not meant to be used outside the module and this should be explicitly noted in the circuit docs. On the other hand, Compact does not offer the same level of robust access controls to circuits as solidity eg private, internal, etc. Circuits are either exported or not.
|
Closing since suggestions were applied piecemeal in the target branch |
This PR suggests some improvements, the main motivation is following OZ Solidity Access Control in the functions signatures and params names.
Refactoring
Removed type aliases —
RoleIdentifier,AdminIdentifier, andAccountIdentifierhave been removed and replaced uniformly withBytes<32>. OnlyRoleCommitment(the Merkle tree leaf type) is retained.Renamed circuits to match OZ Solidity
AccessControlsignatures:proveCallerRolehasRoleassertOnlyRole_checkRole_validateRole_hasRoleCircuit ordering follows the Stepdown Rule — Public/exported circuits appear first, followed by the internal circuits they call. Callers are always defined before callees, so the file reads top-down without forward jumps.
Eliminated redundant init checks — Introduced
_uncheckedGrantRole/_uncheckedRevokeRoleprivate circuits. Export wrappers add the single init check; sibling callers (grantRole,revokeRole,renounceRole) call the unchecked versions directly, giving one init check per call chain.Extracted
_getRoleAdmin/_onlyRole— Shared internal primitives without init checks.New Circuits
hasRole(role, account: ZswapCoinPublicKey)_checkRole(role, account: ZswapCoinPublicKey)computeAccountId(role, account)_computeAccountId_onlyRole(role, accountId)_uncheckedGrantRole_uncheckedRevokeRoleDocumentation
@circuitInfoannotations with current compiler output.commitmentDomain→nullifierDomain.@parammismatch inrenounceRole.assertOnlyRole→_checkRole.Mock Contract
MockShieldedAccessControl.compactupdated to reflect all renames, new circuits, and type alias removals. Circuit order matches the main contract.