Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"!**/tsconfig*.json",
"!**/*.compact",
"!**/artifacts/**/*",
"!**/test-artifacts/**/*",
"!**/coverage/**/*",
"!**/dist/**/*",
"!**/reports/**/*"
Expand Down
1 change: 1 addition & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@openzeppelin-compact/compact": "workspace:^"
},
"devDependencies": {
"@openzeppelin-compact/contracts-simulator": "workspace:^",
"@tsconfig/node22": "^22.0.2",
"@types/node": "22.18.0",
"ts-node": "^10.9.2",
Expand Down
100 changes: 24 additions & 76 deletions contracts/src/access/test/ZOwnablePK.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import {
CompactTypeVector,
convert_bigint_to_Uint8Array,
persistentHash,
transientHash,
upgradeFromTransient,
} from '@midnight-ntwrk/compact-runtime';
import { beforeEach, describe, expect, it } from 'vitest';
import type { ZswapCoinPublicKey } from '../../../artifacts/MockOwnable/contract/index.cjs';
Expand Down Expand Up @@ -101,7 +99,7 @@ describe('ZOwnablePK', () => {
});
});

describe('when not deployed and not initialized', () => {
describe('when not initialized correctly', () => {
const isNotInit = false;

beforeEach(() => {
Expand Down Expand Up @@ -138,36 +136,6 @@ describe('ZOwnablePK', () => {
});
});

describe('when incorrect hashing algo (not SHA256) is used to generate initial owner id', () => {
// ZOwnablePK only supports sha256 for owner id calculation
// Obviously, using any other algo for the id will not work
const badHashAlgo = (pk: ZswapCoinPublicKey, nonce: Uint8Array) => {
const rt_type = new CompactTypeVector(2, new CompactTypeBytes(32));
return upgradeFromTransient(transientHash(rt_type, [pk.bytes, nonce]));
};
const secretNonce = ZOwnablePKPrivateState.generate().secretNonce;
const badOwnerId = badHashAlgo(Z_OWNER, secretNonce);

beforeEach(() => {
ownable = new ZOwnablePKSimulator(badOwnerId, INSTANCE_SALT, isInit);
});
//
type FailingCircuits = [method: keyof ZOwnablePKSimulator, args: unknown[]];
const protectedCircuits: FailingCircuits[] = [
['assertOnlyOwner', []],
['transferOwnership', [badOwnerId]],
['renounceOwnership', []],
];

it.each(protectedCircuits)('%s should fail', (circuitName, args) => {
ownable.callerCtx.setCaller(OWNER);

expect(() => {
(ownable[circuitName] as (...args: unknown[]) => unknown)(...args);
}).toThrow('ZOwnablePK: caller is not the owner');
});
});

describe('after initialization', () => {
beforeEach(() => {
// Create private state object and generate nonce
Expand Down Expand Up @@ -216,40 +184,34 @@ describe('ZOwnablePK', () => {
});

it('should transfer ownership', () => {
ownable.callerCtx.setCaller(OWNER);
ownable.transferOwnership(newIdHash);
ownable.as(OWNER).transferOwnership(newIdHash);
expect(ownable.owner()).toEqual(newOwnerCommitment);

// Old owner
ownable.callerCtx.setCaller(OWNER);
expect(() => {
ownable.assertOnlyOwner();
ownable.as(OWNER).assertOnlyOwner();
}).toThrow('ZOwnablePK: caller is not the owner');

// Unauthorized
ownable.callerCtx.setCaller(UNAUTHORIZED);
expect(() => {
ownable.assertOnlyOwner();
ownable.as(UNAUTHORIZED).assertOnlyOwner();
}).toThrow('ZOwnablePK: caller is not the owner');

// New owner
ownable.callerCtx.setCaller(NEW_OWNER);
ownable.privateState.injectSecretNonce(Buffer.from(newOwnerNonce));
expect(ownable.assertOnlyOwner()).not.to.throw;
expect(ownable.as(NEW_OWNER).assertOnlyOwner()).not.to.throw;
});

it('should fail when transferring to id zero', () => {
ownable.callerCtx.setCaller(OWNER);
const badId = new Uint8Array(32).fill(0);
expect(() => {
ownable.transferOwnership(badId);
ownable.as(OWNER).transferOwnership(badId);
}).toThrow('ZOwnablePK: invalid id');
});

it('should fail when unauthorized transfers ownership', () => {
ownable.callerCtx.setCaller(UNAUTHORIZED);
expect(() => {
ownable.transferOwnership(newOwnerCommitment);
ownable.as(UNAUTHORIZED).transferOwnership(newOwnerCommitment);
}).toThrow('ZOwnablePK: caller is not the owner');
});

Expand All @@ -260,8 +222,7 @@ describe('ZOwnablePK', () => {
const beforeInstance = ownable.getPublicState().ZOwnablePK__counter;

// Transfer
ownable.callerCtx.setCaller(OWNER);
ownable.transferOwnership(newOwnerCommitment);
ownable.as(OWNER).transferOwnership(newOwnerCommitment);

// Check counter
const afterInstance = ownable.getPublicState().ZOwnablePK__counter;
Expand All @@ -280,8 +241,7 @@ describe('ZOwnablePK', () => {
expect(initCommitment).toEqual(expInitCommitment);

// Transfer ownership to self with the same id -> `H(pk, nonce)`
ownable.callerCtx.setCaller(OWNER);
ownable.transferOwnership(repeatedId);
ownable.as(OWNER).transferOwnership(repeatedId);

// Check commitments don't match
const newCommitment = ownable.owner();
Expand All @@ -297,46 +257,41 @@ describe('ZOwnablePK', () => {
expect(newCommitment).toEqual(expNewCommitment);

// Check same owner maintains permissions after transfer
ownable.callerCtx.setCaller(OWNER);
expect(ownable.assertOnlyOwner()).not.to.throw;
expect(ownable.as(OWNER).assertOnlyOwner()).not.to.throw;
});
});

describe('renounceOwnership', () => {
it('should renounce ownership', () => {
ownable.callerCtx.setCaller(OWNER);
ownable.renounceOwnership();
ownable.as(OWNER).renounceOwnership();

// Check owner is reset
expect(ownable.owner()).toEqual(new Uint8Array(32).fill(0));

// Check revoked permissions
expect(() => {
ownable.assertOnlyOwner();
ownable.as(OWNER).assertOnlyOwner();
}).toThrow('ZOwnablePK: caller is not the owner');
});

it('should fail when renouncing from unauthorized', () => {
ownable.callerCtx.setCaller(UNAUTHORIZED);
expect(() => {
ownable.renounceOwnership();
}).toThrow('ZOwnablePK: caller is not the owner');
ownable.as(UNAUTHORIZED).renounceOwnership();
});
});

it('should fail when renouncing from authorized with bad nonce', () => {
ownable.callerCtx.setCaller(OWNER);
ownable.privateState.injectSecretNonce(BAD_NONCE);
expect(() => {
ownable.renounceOwnership();
}).toThrow('ZOwnablePK: caller is not the owner');
ownable.as(OWNER).renounceOwnership();
});
});

it('should fail when renouncing from unauthorized with bad nonce', () => {
ownable.callerCtx.setCaller(UNAUTHORIZED);
ownable.privateState.injectSecretNonce(BAD_NONCE);
expect(() => {
ownable.renounceOwnership();
}).toThrow('ZOwnablePK: caller is not the owner');
ownable.as(UNAUTHORIZED).renounceOwnership();
});
});
});

Expand All @@ -347,8 +302,7 @@ describe('ZOwnablePK', () => {
secretNonce,
);

ownable.callerCtx.setCaller(OWNER);
expect(ownable.assertOnlyOwner()).to.not.throw;
expect(ownable.as(OWNER).assertOnlyOwner()).to.not.throw;
});

it('should fail when the authorized caller has the wrong nonce', () => {
Expand All @@ -361,9 +315,8 @@ describe('ZOwnablePK', () => {
);

// Set caller and call circuit
ownable.callerCtx.setCaller(OWNER);
expect(() => {
ownable.assertOnlyOwner();
ownable.as(OWNER).assertOnlyOwner();
}).toThrow('ZOwnablePK: caller is not the owner');
});

Expand All @@ -373,9 +326,8 @@ describe('ZOwnablePK', () => {
secretNonce,
);

ownable.callerCtx.setCaller(UNAUTHORIZED);
expect(() => {
ownable.assertOnlyOwner();
ownable.as(UNAUTHORIZED).assertOnlyOwner();
}).toThrow('ZOwnablePK: caller is not the owner');
});

Expand All @@ -389,9 +341,8 @@ describe('ZOwnablePK', () => {
);

// Set unauthorized caller and call circuit
ownable.callerCtx.setCaller(UNAUTHORIZED);
expect(() => {
ownable.assertOnlyOwner();
ownable.as(UNAUTHORIZED).assertOnlyOwner();
}).toThrow('ZOwnablePK: caller is not the owner');
});
});
Expand Down Expand Up @@ -522,12 +473,9 @@ describe('ZOwnablePK', () => {
});

it('should allow anyone to transfer', () => {
ownable.callerCtx.setCaller(OWNER);
const id = createIdHash(Z_OWNER, secretNonce);
expect(ownable._transferOwnership(id)).not.to.throw;

ownable.callerCtx.setCaller(UNAUTHORIZED);
expect(ownable._transferOwnership(id)).not.to.throw;
expect(ownable.as(OWNER)._transferOwnership(id)).not.to.throw;
expect(ownable.as(UNAUTHORIZED)._transferOwnership(id)).not.to.throw;
});
});
});
Expand Down
Loading
Loading