Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b0d306c
move compact to packages dir
andrew-fleming Sep 13, 2025
009c5dc
add simulator package
andrew-fleming Sep 15, 2025
8eb34be
fix fmt
andrew-fleming Sep 15, 2025
eb011ab
bump yarn.lock
andrew-fleming Sep 15, 2025
52f9ddc
add simulator package dep
andrew-fleming Sep 15, 2025
fae4508
apply simulator to ZOwnablePK sim and tests
andrew-fleming Sep 15, 2025
43bb2ba
improve sim package name
andrew-fleming Oct 10, 2025
fc4a05f
add test-artifacts
andrew-fleming Oct 10, 2025
bb2f15f
change StateManager -> CircuitContextManager
andrew-fleming Oct 10, 2025
d7ab16a
fix dir name
andrew-fleming Oct 10, 2025
e7d94e1
update artifacts
andrew-fleming Oct 10, 2025
c10b088
fix import
andrew-fleming Oct 10, 2025
3899891
update test artifacts
andrew-fleming Oct 10, 2025
11918f8
biome ignore test-artifacts
andrew-fleming Oct 10, 2025
2247d97
fix fmt
andrew-fleming Oct 10, 2025
6c34eab
fix conflicts
andrew-fleming Oct 10, 2025
fdc5d5d
fix zownable base sim, add comment
andrew-fleming Oct 10, 2025
4d5573c
bump compact version
andrew-fleming Oct 23, 2025
093bcbe
bump compact version in sim
andrew-fleming Oct 23, 2025
8bae0a8
bump compact version
andrew-fleming Oct 23, 2025
67559c9
add account module
andrew-fleming Oct 23, 2025
e6a9294
start poc account testing
andrew-fleming Oct 27, 2025
4fa1990
remove unused vars and imports
andrew-fleming Oct 27, 2025
3031913
improve account comment
andrew-fleming Oct 27, 2025
78cec21
tidy up test
andrew-fleming Oct 27, 2025
747ad57
fix fmt
andrew-fleming Oct 27, 2025
e1b4180
add unsafe to exposed mint, remove burn
andrew-fleming Oct 27, 2025
c178cc8
fix comment
andrew-fleming Oct 27, 2025
7bf0eff
simplify bytes cast
andrew-fleming Oct 27, 2025
29fe2d9
fix comment
andrew-fleming Oct 27, 2025
b018c7b
improve send circuit
andrew-fleming Oct 27, 2025
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Import modules through `node_modules` rather than directly to avoid state confli
```typescript
// MyContract.compact

pragma language_version >= 0.17.0;
pragma language_version >= 0.18.0;

import CompactStandardLibrary;
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/access/Ownable"
Expand Down
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
2 changes: 2 additions & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"scripts": {
"compact": "compact-compiler",
"compact:access": "compact-compiler --dir access",
"compact:account": "compact-compiler --dir account",
"compact:archive": "compact-compiler --dir archive",
"compact:security": "compact-compiler --dir security",
"compact:token": "compact-compiler --dir token",
Expand All @@ -29,6 +30,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
2 changes: 1 addition & 1 deletion contracts/src/access/AccessControl.compact
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Compact Contracts v0.0.1-alpha.0 (access/AccessControl.compact)

pragma language_version >= 0.17.0;
pragma language_version >= 0.18.0;

/**
* @module AccessControl
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/access/Ownable.compact
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Compact Contracts v0.0.1-alpha.0 (access/Ownable.compact)

pragma language_version >= 0.17.0;
pragma language_version >= 0.18.0;

/**
* @module Ownable
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/access/ZOwnablePK.compact
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Compact Contracts v0.0.1-alpha.0 (access/ZOwnablePK.compact)

pragma language_version >= 0.17.0;
pragma language_version >= 0.18.0;

/**
* @module ZOwnablePK
Expand Down
12 changes: 6 additions & 6 deletions contracts/src/access/test/AccessControl.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
type CoinPublicKey,
convert_bigint_to_Uint8Array,
convertFieldToBytes,
} from '@midnight-ntwrk/compact-runtime';
import { beforeEach, describe, expect, it } from 'vitest';
import { AccessControlSimulator } from './simulators/AccessControlSimulator.js';
Expand All @@ -24,11 +24,11 @@ const Z_OPERATOR_CONTRACT =

// Roles
const DEFAULT_ADMIN_ROLE = utils.zeroUint8Array();
const OPERATOR_ROLE_1 = convert_bigint_to_Uint8Array(32, 1n);
const OPERATOR_ROLE_2 = convert_bigint_to_Uint8Array(32, 2n);
const OPERATOR_ROLE_3 = convert_bigint_to_Uint8Array(32, 3n);
const CUSTOM_ADMIN_ROLE = convert_bigint_to_Uint8Array(32, 4n);
const UNINITIALIZED_ROLE = convert_bigint_to_Uint8Array(32, 5n);
const OPERATOR_ROLE_1 = convertFieldToBytes(32, 1n, '123');
const OPERATOR_ROLE_2 = convertFieldToBytes(32, 2n, '123');
const OPERATOR_ROLE_3 = convertFieldToBytes(32, 3n, '123');
const CUSTOM_ADMIN_ROLE = convertFieldToBytes(32, 4n, '123');
const UNINITIALIZED_ROLE = convertFieldToBytes(32, 5n, '123');

let accessControl: AccessControlSimulator;
let caller: CoinPublicKey;
Expand Down
106 changes: 27 additions & 79 deletions contracts/src/access/test/ZOwnablePK.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {
CompactTypeBytes,
CompactTypeVector,
convert_bigint_to_Uint8Array,
convertFieldToBytes,
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 @@ -43,7 +41,7 @@ const buildCommitmentFromId = (
counter: bigint,
): Uint8Array => {
const rt_type = new CompactTypeVector(4, new CompactTypeBytes(32));
const bCounter = convert_bigint_to_Uint8Array(32, counter);
const bCounter = convertFieldToBytes(32, counter, '123');
const bDomain = new TextEncoder().encode(DOMAIN);

const commitment = persistentHash(rt_type, [
Expand All @@ -65,7 +63,7 @@ const buildCommitment = (
const id = createIdHash(pk, nonce);

const rt_type = new CompactTypeVector(4, new CompactTypeBytes(32));
const bCounter = convert_bigint_to_Uint8Array(32, counter);
const bCounter = convertFieldToBytes(32, counter, '123');
const bDomain = new TextEncoder().encode(domain);

const commitment = persistentHash(rt_type, [
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma language_version >= 0.17.0;
pragma language_version >= 0.18.0;

import CompactStandardLibrary;

Expand Down
2 changes: 1 addition & 1 deletion contracts/src/access/test/mocks/MockOwnable.compact
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma language_version >= 0.17.0;
pragma language_version >= 0.18.0;

import CompactStandardLibrary;

Expand Down
2 changes: 1 addition & 1 deletion contracts/src/access/test/mocks/MockZOwnablePK.compact
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma language_version >= 0.17.0;
pragma language_version >= 0.18.0;

import CompactStandardLibrary;
import "../../ZOwnablePK" prefix ZOwnablePK_;
Expand Down
Loading