From 55ba03c2efbc382e8a0585e20a62b2e942ab89e2 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Fri, 27 Mar 2026 14:54:13 +0100 Subject: [PATCH 1/3] chore: same ordering --- .../access/test/ShieldedAccessControl.test.ts | 136 ++++++++++-------- .../ShieldedAccessControlSimulator.ts | 18 +-- 2 files changed, 86 insertions(+), 68 deletions(-) diff --git a/contracts/src/access/test/ShieldedAccessControl.test.ts b/contracts/src/access/test/ShieldedAccessControl.test.ts index 0f0da7c7..cdd4f0d2 100644 --- a/contracts/src/access/test/ShieldedAccessControl.test.ts +++ b/contracts/src/access/test/ShieldedAccessControl.test.ts @@ -530,7 +530,10 @@ describe('ShieldedAccessControl', () => { }); it('should fail when caller provides valid path for a different role, accountId pairing', () => { - shieldedAccessControl._grantRole(OPERATOR_1.role, OPERATOR_1.accountId); + shieldedAccessControl._grantRole( + OPERATOR_1.role, + OPERATOR_1.accountId, + ); // Override witness to return valid path for OPERATOR_1 role commitment shieldedAccessControl.overrideWitness( 'wit_getRoleCommitmentPath', @@ -563,7 +566,9 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._validateRole(ADMIN.role, ADMIN.accountId), ).toBe(true); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(true); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( + true, + ); }); it('when caller has multiple roles', () => { @@ -596,7 +601,9 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._grantRole(OPERATOR_2.role, account2); shieldedAccessControl._grantRole(OPERATOR_3.role, account3); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(true); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( + true, + ); expect(shieldedAccessControl.canProveRole(OPERATOR_1.role)).toBe( true, ); @@ -624,7 +631,9 @@ describe('ShieldedAccessControl', () => { expect(newAdminAccountId).not.toEqual(ADMIN.accountId); shieldedAccessControl._grantRole(ADMIN.role, newAdminAccountId); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(true); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( + true, + ); }); it('when multiple users have the same role', () => { @@ -706,7 +715,9 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._validateRole(ADMIN.role, ADMIN.accountId), ).toBe(false); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( + false, + ); }); it('when revoked role is re-granted', () => { @@ -717,14 +728,18 @@ describe('ShieldedAccessControl', () => { ).toBe(false); shieldedAccessControl._grantRole(ADMIN.role, ADMIN.accountId); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( + false, + ); }); it('when an unauthorized caller has valid nonce', () => { // UNAUTHORIZED uses the same private state (ADMIN.secretNonce for ADMIN.role), // so their derived accountId won't match the committed one. shieldedAccessControl.as(UNAUTHORIZED.publicKey); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( + false, + ); }); it('when an authorized caller provides invalid nonce', () => { @@ -748,7 +763,9 @@ describe('ShieldedAccessControl', () => { ), ); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( + false, + ); }); it('when an authorized caller provides invalid witness path', () => { @@ -765,7 +782,9 @@ describe('ShieldedAccessControl', () => { 'wit_getRoleCommitmentPath', RETURN_BAD_PATH, ); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( + false, + ); }); }); }); @@ -777,7 +796,10 @@ describe('ShieldedAccessControl', () => { }); it('should fail when caller provides valid path for a different role, accountId pairing', () => { - shieldedAccessControl._grantRole(OPERATOR_1.role, OPERATOR_1.accountId); + shieldedAccessControl._grantRole( + OPERATOR_1.role, + OPERATOR_1.accountId, + ); // Override witness to return valid path for OPERATOR_1 role commitment shieldedAccessControl.overrideWitness( 'wit_getRoleCommitmentPath', @@ -848,15 +870,15 @@ describe('ShieldedAccessControl', () => { expect(shieldedAccessControl._uncheckedCanProveRole(ADMIN.role)).toBe( true, ); - expect( - shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role), - ).toBe(true); - expect( - shieldedAccessControl._uncheckedCanProveRole(OPERATOR_2.role), - ).toBe(true); - expect( - shieldedAccessControl._uncheckedCanProveRole(OPERATOR_3.role), - ).toBe(true); + expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role)).toBe( + true, + ); + expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_2.role)).toBe( + true, + ); + expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_3.role)).toBe( + true, + ); }); it('when role is revoked and re-issued with a different accountId', () => { @@ -897,9 +919,9 @@ describe('ShieldedAccessControl', () => { operator1AdminAccountId, ); shieldedAccessControl.as(ADMIN.publicKey); // prove ADMIN has OP_1 role - expect( - shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role), - ).toBe(true); + expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role)).toBe( + true, + ); const operator1Op2AccountId = buildAccountIdHash( OPERATOR_2.zPublicKey, @@ -946,9 +968,9 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._validateRole(OPERATOR_1.role, accountId), ).toBe(false); - expect( - shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role), - ).toBe(false); + expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role)).toBe( + false, + ); }); it('when caller has revoked role', () => { @@ -1590,10 +1612,16 @@ describe('ShieldedAccessControl', () => { ADMIN.secretNonce, ); expect(() => - shieldedAccessControl.renounceRole(OPERATOR_1.role, ADMIN.accountId), + shieldedAccessControl.renounceRole( + OPERATOR_1.role, + ADMIN.accountId, + ), ).not.toThrow(); expect( - shieldedAccessControl._validateRole(OPERATOR_1.role, ADMIN.accountId), + shieldedAccessControl._validateRole( + OPERATOR_1.role, + ADMIN.accountId, + ), ).toBe(false); }); @@ -1617,7 +1645,9 @@ describe('ShieldedAccessControl', () => { it('should fail when unauthorized caller provides valid nonce, and accountId', () => { // check we have valid secret nonce in private state expect( - shieldedAccessControl.privateState.getCurrentSecretNonce(ADMIN.role), + shieldedAccessControl.privateState.getCurrentSecretNonce( + ADMIN.role, + ), ).toEqual(ADMIN.secretNonce); shieldedAccessControl.as(UNAUTHORIZED.publicKey); @@ -2637,7 +2667,10 @@ describe('ShieldedAccessControl', () => { }); it('should fail when wit_getRoleCommitmentPath returns a valid path for a different role, accountId pairing', () => { - shieldedAccessControl._grantRole(OPERATOR_1.role, OPERATOR_1.accountId); + shieldedAccessControl._grantRole( + OPERATOR_1.role, + OPERATOR_1.accountId, + ); // Override witness to return valid path for OPERATOR_1 role commitment shieldedAccessControl.overrideWitness( 'wit_getRoleCommitmentPath', @@ -2786,7 +2819,10 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._grantRole(ADMIN.role, newAdminAccountId); expect( - shieldedAccessControl._validateRole(ADMIN.role, newAdminAccountId), + shieldedAccessControl._validateRole( + ADMIN.role, + newAdminAccountId, + ), ).toBe(true); }); @@ -2880,7 +2916,7 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl.computeRoleCommitment as ( ...args: unknown[] ) => Uint8Array - )(...args), + )(...args) ).not.toEqual(ADMIN.roleCommitment); }); }); @@ -2905,34 +2941,25 @@ describe('ShieldedAccessControl', () => { }); it('should match when authorized caller with correct nonce', () => { - expect(shieldedAccessControl._computeAccountId(ADMIN.role)).toEqual( - ADMIN.accountId, - ); + expect( + shieldedAccessControl._computeAccountId( + ADMIN.role, + ), + ).toEqual(ADMIN.accountId); }); it('should not match when authorized caller with bad nonce', () => { - shieldedAccessControl.privateState.injectSecretNonce( - ADMIN.role, - BAD_INPUT.secretNonce, - ); - const computedAccountId = shieldedAccessControl._computeAccountId( - ADMIN.role, - ); + shieldedAccessControl.privateState.injectSecretNonce(ADMIN.role, BAD_INPUT.secretNonce) + const computedAccountId = shieldedAccessControl._computeAccountId(ADMIN.role) expect(computedAccountId).not.toEqual(ADMIN.accountId); - expect(computedAccountId).toEqual( - buildAccountIdHash(ADMIN.zPublicKey, BAD_INPUT.secretNonce), - ); + expect(computedAccountId).toEqual(buildAccountIdHash(ADMIN.zPublicKey, BAD_INPUT.secretNonce)); }); it('should not match when unauthorized caller with correct nonce', () => { shieldedAccessControl.as(UNAUTHORIZED.publicKey); - const computedAccountId = shieldedAccessControl._computeAccountId( - ADMIN.role, - ); + const computedAccountId = shieldedAccessControl._computeAccountId(ADMIN.role); expect(computedAccountId).not.toEqual(ADMIN.accountId); - expect(computedAccountId).toEqual( - buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce), - ); + expect(computedAccountId).toEqual(buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce)); }); }); @@ -2954,9 +2981,7 @@ describe('ShieldedAccessControl', () => { INSTANCE_SALT, ); expect(computedAccountId).not.toEqual(ADMIN.accountId); - expect(computedAccountId).toEqual( - buildAccountIdHash(ADMIN.zPublicKey, BAD_INPUT.secretNonce), - ); + expect(computedAccountId).toEqual(buildAccountIdHash(ADMIN.zPublicKey, BAD_INPUT.secretNonce)); }); it('should not match when given unauthorized account with correct nonce', () => { @@ -2966,10 +2991,9 @@ describe('ShieldedAccessControl', () => { INSTANCE_SALT, ); expect(computedAccountId).not.toEqual(ADMIN.accountId); - expect(computedAccountId).toEqual( - buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce), - ); + expect(computedAccountId).toEqual(buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce)); }); }); + }); }); diff --git a/contracts/src/access/test/simulators/ShieldedAccessControlSimulator.ts b/contracts/src/access/test/simulators/ShieldedAccessControlSimulator.ts index 6aa533aa..8ea6d558 100644 --- a/contracts/src/access/test/simulators/ShieldedAccessControlSimulator.ts +++ b/contracts/src/access/test/simulators/ShieldedAccessControlSimulator.ts @@ -93,11 +93,7 @@ export class ShieldedAccessControlSimulator extends ShieldedAccessControlSimulat return this.circuits.impure._revokeRole(role, accountId); } - public _updateRole( - role: Uint8Array, - accountId: Uint8Array, - updateType: UpdateType, - ) { + public _updateRole(role: Uint8Array, accountId: Uint8Array, updateType: UpdateType) { return this.circuits.impure._updateRole(role, accountId, updateType); } @@ -124,20 +120,18 @@ export class ShieldedAccessControlSimulator extends ShieldedAccessControlSimulat return this.circuits.pure.computeNullifier(roleCommitment); } - public _computeAccountId(role: Uint8Array): Uint8Array { + public _computeAccountId( + role: Uint8Array, + ): Uint8Array { return this.circuits.impure._computeAccountId(role); } public computeAccountId( account: ZswapCoinPublicKey, secretNonce: Uint8Array, - instanceSalt: Uint8Array, + instanceSalt: Uint8Array ): Uint8Array { - return this.circuits.pure.computeAccountId( - account, - secretNonce, - instanceSalt, - ); + return this.circuits.pure.computeAccountId(account, secretNonce, instanceSalt); } public readonly privateState = { From f25cfabd2e259437a5fda5dbc79cd132299616db Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Fri, 27 Mar 2026 14:54:52 +0100 Subject: [PATCH 2/3] chore: lint --- .../access/test/ShieldedAccessControl.test.ts | 136 ++++++++---------- .../ShieldedAccessControlSimulator.ts | 18 ++- 2 files changed, 68 insertions(+), 86 deletions(-) diff --git a/contracts/src/access/test/ShieldedAccessControl.test.ts b/contracts/src/access/test/ShieldedAccessControl.test.ts index cdd4f0d2..0f0da7c7 100644 --- a/contracts/src/access/test/ShieldedAccessControl.test.ts +++ b/contracts/src/access/test/ShieldedAccessControl.test.ts @@ -530,10 +530,7 @@ describe('ShieldedAccessControl', () => { }); it('should fail when caller provides valid path for a different role, accountId pairing', () => { - shieldedAccessControl._grantRole( - OPERATOR_1.role, - OPERATOR_1.accountId, - ); + shieldedAccessControl._grantRole(OPERATOR_1.role, OPERATOR_1.accountId); // Override witness to return valid path for OPERATOR_1 role commitment shieldedAccessControl.overrideWitness( 'wit_getRoleCommitmentPath', @@ -566,9 +563,7 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._validateRole(ADMIN.role, ADMIN.accountId), ).toBe(true); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( - true, - ); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(true); }); it('when caller has multiple roles', () => { @@ -601,9 +596,7 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._grantRole(OPERATOR_2.role, account2); shieldedAccessControl._grantRole(OPERATOR_3.role, account3); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( - true, - ); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(true); expect(shieldedAccessControl.canProveRole(OPERATOR_1.role)).toBe( true, ); @@ -631,9 +624,7 @@ describe('ShieldedAccessControl', () => { expect(newAdminAccountId).not.toEqual(ADMIN.accountId); shieldedAccessControl._grantRole(ADMIN.role, newAdminAccountId); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( - true, - ); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(true); }); it('when multiple users have the same role', () => { @@ -715,9 +706,7 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._validateRole(ADMIN.role, ADMIN.accountId), ).toBe(false); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( - false, - ); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); }); it('when revoked role is re-granted', () => { @@ -728,18 +717,14 @@ describe('ShieldedAccessControl', () => { ).toBe(false); shieldedAccessControl._grantRole(ADMIN.role, ADMIN.accountId); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( - false, - ); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); }); it('when an unauthorized caller has valid nonce', () => { // UNAUTHORIZED uses the same private state (ADMIN.secretNonce for ADMIN.role), // so their derived accountId won't match the committed one. shieldedAccessControl.as(UNAUTHORIZED.publicKey); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( - false, - ); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); }); it('when an authorized caller provides invalid nonce', () => { @@ -763,9 +748,7 @@ describe('ShieldedAccessControl', () => { ), ); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( - false, - ); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); }); it('when an authorized caller provides invalid witness path', () => { @@ -782,9 +765,7 @@ describe('ShieldedAccessControl', () => { 'wit_getRoleCommitmentPath', RETURN_BAD_PATH, ); - expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe( - false, - ); + expect(shieldedAccessControl.canProveRole(ADMIN.role)).toBe(false); }); }); }); @@ -796,10 +777,7 @@ describe('ShieldedAccessControl', () => { }); it('should fail when caller provides valid path for a different role, accountId pairing', () => { - shieldedAccessControl._grantRole( - OPERATOR_1.role, - OPERATOR_1.accountId, - ); + shieldedAccessControl._grantRole(OPERATOR_1.role, OPERATOR_1.accountId); // Override witness to return valid path for OPERATOR_1 role commitment shieldedAccessControl.overrideWitness( 'wit_getRoleCommitmentPath', @@ -870,15 +848,15 @@ describe('ShieldedAccessControl', () => { expect(shieldedAccessControl._uncheckedCanProveRole(ADMIN.role)).toBe( true, ); - expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role)).toBe( - true, - ); - expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_2.role)).toBe( - true, - ); - expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_3.role)).toBe( - true, - ); + expect( + shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role), + ).toBe(true); + expect( + shieldedAccessControl._uncheckedCanProveRole(OPERATOR_2.role), + ).toBe(true); + expect( + shieldedAccessControl._uncheckedCanProveRole(OPERATOR_3.role), + ).toBe(true); }); it('when role is revoked and re-issued with a different accountId', () => { @@ -919,9 +897,9 @@ describe('ShieldedAccessControl', () => { operator1AdminAccountId, ); shieldedAccessControl.as(ADMIN.publicKey); // prove ADMIN has OP_1 role - expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role)).toBe( - true, - ); + expect( + shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role), + ).toBe(true); const operator1Op2AccountId = buildAccountIdHash( OPERATOR_2.zPublicKey, @@ -968,9 +946,9 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._validateRole(OPERATOR_1.role, accountId), ).toBe(false); - expect(shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role)).toBe( - false, - ); + expect( + shieldedAccessControl._uncheckedCanProveRole(OPERATOR_1.role), + ).toBe(false); }); it('when caller has revoked role', () => { @@ -1612,16 +1590,10 @@ describe('ShieldedAccessControl', () => { ADMIN.secretNonce, ); expect(() => - shieldedAccessControl.renounceRole( - OPERATOR_1.role, - ADMIN.accountId, - ), + shieldedAccessControl.renounceRole(OPERATOR_1.role, ADMIN.accountId), ).not.toThrow(); expect( - shieldedAccessControl._validateRole( - OPERATOR_1.role, - ADMIN.accountId, - ), + shieldedAccessControl._validateRole(OPERATOR_1.role, ADMIN.accountId), ).toBe(false); }); @@ -1645,9 +1617,7 @@ describe('ShieldedAccessControl', () => { it('should fail when unauthorized caller provides valid nonce, and accountId', () => { // check we have valid secret nonce in private state expect( - shieldedAccessControl.privateState.getCurrentSecretNonce( - ADMIN.role, - ), + shieldedAccessControl.privateState.getCurrentSecretNonce(ADMIN.role), ).toEqual(ADMIN.secretNonce); shieldedAccessControl.as(UNAUTHORIZED.publicKey); @@ -2667,10 +2637,7 @@ describe('ShieldedAccessControl', () => { }); it('should fail when wit_getRoleCommitmentPath returns a valid path for a different role, accountId pairing', () => { - shieldedAccessControl._grantRole( - OPERATOR_1.role, - OPERATOR_1.accountId, - ); + shieldedAccessControl._grantRole(OPERATOR_1.role, OPERATOR_1.accountId); // Override witness to return valid path for OPERATOR_1 role commitment shieldedAccessControl.overrideWitness( 'wit_getRoleCommitmentPath', @@ -2819,10 +2786,7 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl._grantRole(ADMIN.role, newAdminAccountId); expect( - shieldedAccessControl._validateRole( - ADMIN.role, - newAdminAccountId, - ), + shieldedAccessControl._validateRole(ADMIN.role, newAdminAccountId), ).toBe(true); }); @@ -2916,7 +2880,7 @@ describe('ShieldedAccessControl', () => { shieldedAccessControl.computeRoleCommitment as ( ...args: unknown[] ) => Uint8Array - )(...args) + )(...args), ).not.toEqual(ADMIN.roleCommitment); }); }); @@ -2941,25 +2905,34 @@ describe('ShieldedAccessControl', () => { }); it('should match when authorized caller with correct nonce', () => { - expect( - shieldedAccessControl._computeAccountId( - ADMIN.role, - ), - ).toEqual(ADMIN.accountId); + expect(shieldedAccessControl._computeAccountId(ADMIN.role)).toEqual( + ADMIN.accountId, + ); }); it('should not match when authorized caller with bad nonce', () => { - shieldedAccessControl.privateState.injectSecretNonce(ADMIN.role, BAD_INPUT.secretNonce) - const computedAccountId = shieldedAccessControl._computeAccountId(ADMIN.role) + shieldedAccessControl.privateState.injectSecretNonce( + ADMIN.role, + BAD_INPUT.secretNonce, + ); + const computedAccountId = shieldedAccessControl._computeAccountId( + ADMIN.role, + ); expect(computedAccountId).not.toEqual(ADMIN.accountId); - expect(computedAccountId).toEqual(buildAccountIdHash(ADMIN.zPublicKey, BAD_INPUT.secretNonce)); + expect(computedAccountId).toEqual( + buildAccountIdHash(ADMIN.zPublicKey, BAD_INPUT.secretNonce), + ); }); it('should not match when unauthorized caller with correct nonce', () => { shieldedAccessControl.as(UNAUTHORIZED.publicKey); - const computedAccountId = shieldedAccessControl._computeAccountId(ADMIN.role); + const computedAccountId = shieldedAccessControl._computeAccountId( + ADMIN.role, + ); expect(computedAccountId).not.toEqual(ADMIN.accountId); - expect(computedAccountId).toEqual(buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce)); + expect(computedAccountId).toEqual( + buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce), + ); }); }); @@ -2981,7 +2954,9 @@ describe('ShieldedAccessControl', () => { INSTANCE_SALT, ); expect(computedAccountId).not.toEqual(ADMIN.accountId); - expect(computedAccountId).toEqual(buildAccountIdHash(ADMIN.zPublicKey, BAD_INPUT.secretNonce)); + expect(computedAccountId).toEqual( + buildAccountIdHash(ADMIN.zPublicKey, BAD_INPUT.secretNonce), + ); }); it('should not match when given unauthorized account with correct nonce', () => { @@ -2991,9 +2966,10 @@ describe('ShieldedAccessControl', () => { INSTANCE_SALT, ); expect(computedAccountId).not.toEqual(ADMIN.accountId); - expect(computedAccountId).toEqual(buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce)); + expect(computedAccountId).toEqual( + buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce), + ); }); }); - }); }); diff --git a/contracts/src/access/test/simulators/ShieldedAccessControlSimulator.ts b/contracts/src/access/test/simulators/ShieldedAccessControlSimulator.ts index 8ea6d558..6aa533aa 100644 --- a/contracts/src/access/test/simulators/ShieldedAccessControlSimulator.ts +++ b/contracts/src/access/test/simulators/ShieldedAccessControlSimulator.ts @@ -93,7 +93,11 @@ export class ShieldedAccessControlSimulator extends ShieldedAccessControlSimulat return this.circuits.impure._revokeRole(role, accountId); } - public _updateRole(role: Uint8Array, accountId: Uint8Array, updateType: UpdateType) { + public _updateRole( + role: Uint8Array, + accountId: Uint8Array, + updateType: UpdateType, + ) { return this.circuits.impure._updateRole(role, accountId, updateType); } @@ -120,18 +124,20 @@ export class ShieldedAccessControlSimulator extends ShieldedAccessControlSimulat return this.circuits.pure.computeNullifier(roleCommitment); } - public _computeAccountId( - role: Uint8Array, - ): Uint8Array { + public _computeAccountId(role: Uint8Array): Uint8Array { return this.circuits.impure._computeAccountId(role); } public computeAccountId( account: ZswapCoinPublicKey, secretNonce: Uint8Array, - instanceSalt: Uint8Array + instanceSalt: Uint8Array, ): Uint8Array { - return this.circuits.pure.computeAccountId(account, secretNonce, instanceSalt); + return this.circuits.pure.computeAccountId( + account, + secretNonce, + instanceSalt, + ); } public readonly privateState = { From cd52159ac9254f31c4d7a83e8556090de32f7f28 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Fri, 27 Mar 2026 15:15:19 +0100 Subject: [PATCH 3/3] refactor: adding more test cases for shielded access control --- .../access/test/ShieldedAccessControl.test.ts | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/contracts/src/access/test/ShieldedAccessControl.test.ts b/contracts/src/access/test/ShieldedAccessControl.test.ts index 0f0da7c7..29728922 100644 --- a/contracts/src/access/test/ShieldedAccessControl.test.ts +++ b/contracts/src/access/test/ShieldedAccessControl.test.ts @@ -1134,6 +1134,25 @@ describe('ShieldedAccessControl', () => { ), ).toThrow('ShieldedAccessControl: unauthorized account'); }); + + it('when admin role has been reassigned via _setRoleAdmin', () => { + // Make OPERATOR_1.role the admin of OPERATOR_2.role + shieldedAccessControl._setRoleAdmin(OPERATOR_2.role, OPERATOR_1.role); + + shieldedAccessControl.privateState.injectSecretNonce( + OPERATOR_1.role, + ADMIN.secretNonce, + ); + + // ADMIN holds DEFAULT_ADMIN_ROLE but not OPERATOR_1.role, + // so granting OPERATOR_2.role should fail + expect(() => + shieldedAccessControl.grantRole( + OPERATOR_2.role, + OPERATOR_2.accountId, + ), + ).toThrow('ShieldedAccessControl: unauthorized account'); + }); }); describe('should not update _operatorRoles Merkle tree', () => { @@ -1657,6 +1676,32 @@ describe('ShieldedAccessControl', () => { ), ).toBe(true); }); + + it('should permanently block re-grant to the same accountId after renounce', () => { + shieldedAccessControl.renounceRole(ADMIN.role, ADMIN.accountId); + + // re-grant with same accountId — nullifier blocks it + shieldedAccessControl._grantRole(ADMIN.role, ADMIN.accountId); + expect( + shieldedAccessControl._validateRole(ADMIN.role, ADMIN.accountId), + ).toBe(false); + }); + + it('should allow re-grant with a new accountId after renounce', () => { + shieldedAccessControl.renounceRole(ADMIN.role, ADMIN.accountId); + + const newNonce = Buffer.alloc(32, 'NEW_ADMIN_NONCE'); + shieldedAccessControl.privateState.injectSecretNonce( + ADMIN.role, + newNonce, + ); + const newAccountId = buildAccountIdHash(ADMIN.zPublicKey, newNonce); + shieldedAccessControl._grantRole(ADMIN.role, newAccountId); + + expect( + shieldedAccessControl._validateRole(ADMIN.role, newAccountId), + ).toBe(true); + }); }); describe('revokeRole', () => { @@ -1979,6 +2024,27 @@ describe('ShieldedAccessControl', () => { ), ).toBe(false); }); + + it('when admin self-revokes then cannot further grant or revoke', () => { + shieldedAccessControl.revokeRole(ADMIN.role, ADMIN.accountId); + expect( + shieldedAccessControl._validateRole(ADMIN.role, ADMIN.accountId), + ).toBe(false); + + expect(() => + shieldedAccessControl.grantRole( + OPERATOR_1.role, + OPERATOR_1.accountId, + ), + ).toThrow('ShieldedAccessControl: unauthorized account'); + + expect(() => + shieldedAccessControl.revokeRole( + OPERATOR_1.role, + OPERATOR_1.accountId, + ), + ).toThrow('ShieldedAccessControl: unauthorized account'); + }); }); }); @@ -2367,6 +2433,27 @@ describe('ShieldedAccessControl', () => { ), ).toBe(true); }); + + it('when granting a different accountId for a role whose previous accountId was revoked', () => { + shieldedAccessControl._updateRole( + ADMIN.role, + ADMIN.accountId, + UpdateType.Revoke, + ); + expect( + shieldedAccessControl._updateRole( + ADMIN.role, + OPERATOR_1.accountId, + UpdateType.Grant, + ), + ).toBe(true); + expect( + shieldedAccessControl._validateRole( + ADMIN.role, + OPERATOR_1.accountId, + ), + ).toBe(true); + }); }); describe('should update _operatorRoles merkle tree', () => { @@ -2628,6 +2715,31 @@ describe('ShieldedAccessControl', () => { new Uint8Array(OPERATOR_2.role), ); }); + + it('should return DEFAULT_ADMIN_ROLE when admin is explicitly set to zero bytes', () => { + // Set a custom admin first, then reset to zero bytes (DEFAULT_ADMIN_ROLE) + shieldedAccessControl._setRoleAdmin(OPERATOR_1.role, ADMIN.role); + shieldedAccessControl._setRoleAdmin( + OPERATOR_1.role, + new Uint8Array(32), + ); + + // getRoleAdmin takes the map-lookup path (member is true) but returns zero bytes, + // which should equal DEFAULT_ADMIN_ROLE + expect( + shieldedAccessControl.getRoleAdmin(OPERATOR_1.role), + ).toStrictEqual(new Uint8Array(32)); + expect( + shieldedAccessControl.getRoleAdmin(OPERATOR_1.role), + ).toStrictEqual(shieldedAccessControl.DEFAULT_ADMIN_ROLE()); + }); + + it('should allow a role to be set as its own admin', () => { + shieldedAccessControl._setRoleAdmin(OPERATOR_1.role, OPERATOR_1.role); + expect(shieldedAccessControl.getRoleAdmin(OPERATOR_1.role)).toEqual( + new Uint8Array(OPERATOR_1.role), + ); + }); }); describe('_validateRole', () => { @@ -2883,6 +2995,30 @@ describe('ShieldedAccessControl', () => { )(...args), ).not.toEqual(ADMIN.roleCommitment); }); + + it('should produce a different commitment for the same (role, accountId) when instanceSalt differs', () => { + const differentSalt = new Uint8Array(32).fill(1); + const otherInstance = new ShieldedAccessControlSimulator( + differentSalt, + true, + { + privateState: ShieldedAccessControlPrivateState.withRoleAndNonce( + ADMIN.role, + ADMIN.secretNonce, + ), + }, + ); + + const commitment1 = shieldedAccessControl.computeRoleCommitment( + ADMIN.role, + ADMIN.accountId, + ); + const commitment2 = otherInstance.computeRoleCommitment( + ADMIN.role, + ADMIN.accountId, + ); + expect(commitment1).not.toEqual(commitment2); + }); }); describe('computeNullifier', () => { @@ -2970,6 +3106,32 @@ describe('ShieldedAccessControl', () => { buildAccountIdHash(UNAUTHORIZED.zPublicKey, ADMIN.secretNonce), ); }); + + it('should produce a different accountId for the same (account, nonce) when instanceSalt differs', () => { + const differentSalt = new Uint8Array(32).fill(1); + const accountId1 = shieldedAccessControl.computeAccountId( + ADMIN.zPublicKey, + ADMIN.secretNonce, + INSTANCE_SALT, + ); + const accountId2 = shieldedAccessControl.computeAccountId( + ADMIN.zPublicKey, + ADMIN.secretNonce, + differentSalt, + ); + expect(accountId1).not.toEqual(accountId2); + }); + + it('should succeed when secretNonce is zero bytes', () => { + const zeroNonce = new Uint8Array(32); + expect( + shieldedAccessControl.computeAccountId( + ADMIN.zPublicKey, + zeroNonce, + INSTANCE_SALT, + ), + ).toEqual(buildAccountIdHash(ADMIN.zPublicKey, zeroNonce)); + }); }); }); });