Skip to content

Commit bb7089f

Browse files
committed
refactor: enhance the strcuture and folder namings
1 parent 03e396b commit bb7089f

16 files changed

Lines changed: 157 additions & 3 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ make env-down # when finished
181181
The dominant cost is per-describe wallet sync; iterating on a single spec is much faster than running everything. Filter to one file via vitest's `--config` invocation directly if you're in `contracts/`:
182182
183183
```bash
184-
cd contracts && yarn test:integration:watch -- specs/authority/freeze.spec.ts
184+
cd contracts && yarn test:integration:watch -- specs/cma/freeze.spec.ts
185185
```
186186
187187
### Check/apply Biome formatter

contracts/test/integration/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ End-to-end specs that drive the OpenZeppelin Compact modules against a real loca
44

55
## Structure
66

7-
- **`specs/`** — what runs in CI. Grouped by surface under test (`accessControl/`, `authority/`, `verifierKey/`, plus a top-level `smoke.spec.ts`).
7+
- **`specs/`** — what runs in CI. Grouped by surface under test (`accessControl/`, `cma/`, `upgrades/`, plus a top-level `smoke.spec.ts`).
88
- **`fixtures/`** — per-contract deploy + handle factories. `testTokenV1.ts` returns a kit (deployer wallet, signer pool, ledger reader); `testTokenV2.ts` exposes `bindAsV2(kit, alias)` for the upgrade specs.
99
- **`_harness/`** — cross-cutting helpers: CMA wrappers (`cma.ts`), provider builders, network config, the shared `WalletPool` (singleton across the suite).
1010
- **`_mocks/`** — test-only `.compact` contracts (the `TestToken` composite, V1 and V2).
@@ -26,5 +26,6 @@ Working record of what we've learned about Compact's CMA / VK upgrade pathway fr
2626
| Q7 | `ReplaceAuthority` mixed with other `SingleUpdate` kinds in one bundle? || Chain rejects in both orderings (`Custom error: 117`). Together with Q2, suggests the rule "any bundle containing a `ReplaceAuthority` must contain *only* that one SU." Pinned in `mixedBundle.spec.ts`. |
2727
| Q8 | Cross-contract signature replay (sign for A, address to B)? || Chain rejects — `dataToSign` is address-bound. Pinned in `crossContractReplay.spec.ts`. |
2828
| Q9 | Empty-committee `ReplaceAuthority(committee=[], threshold=1)` accepted by chain? || No. Chain rejects at submission (`Custom error: 117`). The "abandoned-key" workaround in `freeze.spec.ts` is therefore the only viable freeze pattern. Pinned in `emptyCommitteeFreeze.spec.ts`. |
29+
| Q10 | VK-only multi-update bundles on **different** ops (Insert+Insert, Remove+Remove, Insert+Remove)? | (pending run) | Tested in `multiVkBundle.spec.ts`. Pinning policy: assertions assume entire success (chain accepts the realistic upgrade path). Update this row after first green run. |
2930

3031
Status: ✅ Answered · ◐ Partial · ⏳ Open

contracts/test/integration/_harness/cma.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export async function freeze<C extends ContractNs.Any>(
201201
*
202202
* @returns the `FinalizedTxData` from `submitTx`. Throws on submission
203203
* failure (`TxFailedError` from the SDK or wrapped variants — see
204-
* existing patterns in `specs/authority/`).
204+
* existing patterns in `specs/cma/`).
205205
*
206206
* @example
207207
* await submitRawMaintenanceUpdate(kit.providers, kit.contractAddress, [

contracts/test/integration/specs/authority/crossContractReplay.spec.ts renamed to contracts/test/integration/specs/cma/crossContractReplay.spec.ts

File renamed without changes.

contracts/test/integration/specs/authority/emptyCommitteeFreeze.spec.ts renamed to contracts/test/integration/specs/cma/emptyCommitteeFreeze.spec.ts

File renamed without changes.

contracts/test/integration/specs/authority/freeze.spec.ts renamed to contracts/test/integration/specs/cma/freeze.spec.ts

File renamed without changes.

contracts/test/integration/specs/authority/mixedBundle.spec.ts renamed to contracts/test/integration/specs/cma/mixedBundle.spec.ts

File renamed without changes.

contracts/test/integration/specs/authority/multiUpdate.spec.ts renamed to contracts/test/integration/specs/cma/multiUpdate.spec.ts

File renamed without changes.
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import {
2+
ContractOperationVersion,
3+
ContractOperationVersionedVerifierKey,
4+
VerifierKeyInsert,
5+
VerifierKeyRemove,
6+
} from '@midnight-ntwrk/ledger-v8';
7+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
8+
import { submitRawMaintenanceUpdate } from '../../_harness/cma.js';
9+
import {
10+
deployTestTokenV1,
11+
type TestTokenV1Kit,
12+
} from '../../fixtures/testTokenV1.js';
13+
14+
/**
15+
* Spec: bundle-shape matrix for non-`ReplaceAuthority` `MaintenanceUpdate`s.
16+
*
17+
* What we know going in (from earlier specs):
18+
* - Multi-`ReplaceAuthority` in one bundle: rejected at submission ([`multiUpdate.spec.ts`](./multiUpdate.spec.ts) Q2).
19+
* - Multi-`VerifierKeyInsert` on the **same** op: tx finalises but the
20+
* bundle reverts atomically with `status: 'FailFallible'`
21+
* ([`multiUpdate.spec.ts`](./multiUpdate.spec.ts) Q4 chain-level).
22+
* - `ReplaceAuthority` mixed with another `SingleUpdate` kind: rejected
23+
* in either order ([`mixedBundle.spec.ts`](./mixedBundle.spec.ts) Q7).
24+
*
25+
* What this spec fills in: the three bundle shapes that remain — VK-only
26+
* bundles on **different** operations. These are the realistic happy path
27+
* for a multi-circuit version bump (e.g., simultaneously rotating `_mint`
28+
* and `pause` VKs in one tx). The suite has implicitly assumed they work
29+
* but never directly confirmed it.
30+
*
31+
* Three describes, each its own fresh deploy (the bundles mutate state
32+
* and we want each test in a known-clean starting state):
33+
*
34+
* 1. **Multi-insert on different ops** — `[Insert(_mint), Insert(pause)]`
35+
* against empty slots. Expect entire success.
36+
* 2. **Multi-remove on different ops** — `[Remove(_mint), Remove(pause)]`
37+
* against occupied slots. Expect entire success.
38+
* 3. **Mixed `Insert` + `Remove` on different ops** — `[Insert(_mint),
39+
* Remove(pause)]`. Q7 only forbade mixing with `ReplaceAuthority`;
40+
* mixing VK kinds should be allowed. Expect entire success.
41+
*
42+
* If any describe fails, we pin to the observed behaviour and update the
43+
* [README notes table](../../README.md#notes--open-questions). Until then,
44+
* Q10 (this whole probe) is the spec's contribution.
45+
*/
46+
describe('TestToken — multi-VK bundles on different ops', () => {
47+
describe('multi-insert on different empty slots', () => {
48+
let v1: TestTokenV1Kit;
49+
50+
beforeAll(async () => {
51+
v1 = await deployTestTokenV1();
52+
// Empty both target slots so the inserts land cleanly.
53+
await v1.deployed.circuitMaintenanceTx._mint.removeVerifierKey();
54+
await v1.deployed.circuitMaintenanceTx.pause.removeVerifierKey();
55+
});
56+
57+
afterAll(async () => {
58+
await v1?.teardown();
59+
});
60+
61+
it('should accept the bundle entirely; both slots become occupied', async () => {
62+
const mintVk = await v1.providers.zkConfigProvider.getVerifierKey('_mint');
63+
const pauseVk = await v1.providers.zkConfigProvider.getVerifierKey('pause');
64+
const versionedMintVk = new ContractOperationVersionedVerifierKey('v3', mintVk);
65+
const versionedPauseVk = new ContractOperationVersionedVerifierKey('v3', pauseVk);
66+
67+
const result = await submitRawMaintenanceUpdate(
68+
v1.providers,
69+
v1.contractAddress,
70+
[
71+
new VerifierKeyInsert('_mint', versionedMintVk),
72+
new VerifierKeyInsert('pause', versionedPauseVk),
73+
],
74+
);
75+
expect(result.status).toBe('SucceedEntirely');
76+
77+
const stateAfter = await v1.providers.publicDataProvider.queryContractState(
78+
v1.contractAddress,
79+
);
80+
expect(stateAfter?.operation('_mint')).toBeDefined();
81+
expect(stateAfter?.operation('pause')).toBeDefined();
82+
});
83+
});
84+
85+
describe('multi-remove on different occupied slots', () => {
86+
let v1: TestTokenV1Kit;
87+
88+
beforeAll(async () => {
89+
// Fresh deploy: both slots are occupied with their original VKs.
90+
v1 = await deployTestTokenV1();
91+
});
92+
93+
afterAll(async () => {
94+
await v1?.teardown();
95+
});
96+
97+
it('should accept the bundle entirely; both slots become empty', async () => {
98+
const v3 = new ContractOperationVersion('v3');
99+
const result = await submitRawMaintenanceUpdate(
100+
v1.providers,
101+
v1.contractAddress,
102+
[
103+
new VerifierKeyRemove('_mint', v3),
104+
new VerifierKeyRemove('pause', v3),
105+
],
106+
);
107+
expect(result.status).toBe('SucceedEntirely');
108+
109+
const stateAfter = await v1.providers.publicDataProvider.queryContractState(
110+
v1.contractAddress,
111+
);
112+
expect(stateAfter?.operation('_mint')).toBeUndefined();
113+
expect(stateAfter?.operation('pause')).toBeUndefined();
114+
});
115+
});
116+
117+
describe('mixed Insert + Remove on different ops', () => {
118+
let v1: TestTokenV1Kit;
119+
120+
beforeAll(async () => {
121+
v1 = await deployTestTokenV1();
122+
// Empty `_mint` so the bundle's Insert can land; leave `pause`
123+
// occupied so the bundle's Remove has something to remove.
124+
await v1.deployed.circuitMaintenanceTx._mint.removeVerifierKey();
125+
});
126+
127+
afterAll(async () => {
128+
await v1?.teardown();
129+
});
130+
131+
it('should accept the bundle entirely; `_mint` becomes occupied, `pause` becomes empty', async () => {
132+
const mintVk = await v1.providers.zkConfigProvider.getVerifierKey('_mint');
133+
const versionedMintVk = new ContractOperationVersionedVerifierKey('v3', mintVk);
134+
const v3 = new ContractOperationVersion('v3');
135+
136+
const result = await submitRawMaintenanceUpdate(
137+
v1.providers,
138+
v1.contractAddress,
139+
[
140+
new VerifierKeyInsert('_mint', versionedMintVk),
141+
new VerifierKeyRemove('pause', v3),
142+
],
143+
);
144+
expect(result.status).toBe('SucceedEntirely');
145+
146+
const stateAfter = await v1.providers.publicDataProvider.queryContractState(
147+
v1.contractAddress,
148+
);
149+
expect(stateAfter?.operation('_mint')).toBeDefined();
150+
expect(stateAfter?.operation('pause')).toBeUndefined();
151+
});
152+
});
153+
});

contracts/test/integration/specs/authority/rotation.spec.ts renamed to contracts/test/integration/specs/cma/rotation.spec.ts

File renamed without changes.

0 commit comments

Comments
 (0)