@fluxpointstudios/orynq-sdk-midnight-prover 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +78 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +89 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/linking/cardano-anchor-link.d.ts +250 -0
  7. package/dist/linking/cardano-anchor-link.d.ts.map +1 -0
  8. package/dist/linking/cardano-anchor-link.js +447 -0
  9. package/dist/linking/cardano-anchor-link.js.map +1 -0
  10. package/dist/linking/index.d.ts +33 -0
  11. package/dist/linking/index.d.ts.map +1 -0
  12. package/dist/linking/index.js +31 -0
  13. package/dist/linking/index.js.map +1 -0
  14. package/dist/linking/proof-publication.d.ts +217 -0
  15. package/dist/linking/proof-publication.d.ts.map +1 -0
  16. package/dist/linking/proof-publication.js +385 -0
  17. package/dist/linking/proof-publication.js.map +1 -0
  18. package/dist/midnight/index.d.ts +30 -0
  19. package/dist/midnight/index.d.ts.map +1 -0
  20. package/dist/midnight/index.js +27 -0
  21. package/dist/midnight/index.js.map +1 -0
  22. package/dist/midnight/proof-server-client.d.ts +236 -0
  23. package/dist/midnight/proof-server-client.d.ts.map +1 -0
  24. package/dist/midnight/proof-server-client.js +422 -0
  25. package/dist/midnight/proof-server-client.js.map +1 -0
  26. package/dist/midnight/public-inputs.d.ts +134 -0
  27. package/dist/midnight/public-inputs.d.ts.map +1 -0
  28. package/dist/midnight/public-inputs.js +338 -0
  29. package/dist/midnight/public-inputs.js.map +1 -0
  30. package/dist/midnight/witness-builder.d.ts +119 -0
  31. package/dist/midnight/witness-builder.d.ts.map +1 -0
  32. package/dist/midnight/witness-builder.js +238 -0
  33. package/dist/midnight/witness-builder.js.map +1 -0
  34. package/dist/proofs/hash-chain-proof.d.ts +171 -0
  35. package/dist/proofs/hash-chain-proof.d.ts.map +1 -0
  36. package/dist/proofs/hash-chain-proof.js +437 -0
  37. package/dist/proofs/hash-chain-proof.js.map +1 -0
  38. package/dist/proofs/index.d.ts +35 -0
  39. package/dist/proofs/index.d.ts.map +1 -0
  40. package/dist/proofs/index.js +34 -0
  41. package/dist/proofs/index.js.map +1 -0
  42. package/dist/proofs/policy-compliance-proof.d.ts +165 -0
  43. package/dist/proofs/policy-compliance-proof.d.ts.map +1 -0
  44. package/dist/proofs/policy-compliance-proof.js +514 -0
  45. package/dist/proofs/policy-compliance-proof.js.map +1 -0
  46. package/dist/proofs/selective-disclosure.d.ts +213 -0
  47. package/dist/proofs/selective-disclosure.d.ts.map +1 -0
  48. package/dist/proofs/selective-disclosure.js +629 -0
  49. package/dist/proofs/selective-disclosure.js.map +1 -0
  50. package/dist/prover-interface.d.ts +288 -0
  51. package/dist/prover-interface.d.ts.map +1 -0
  52. package/dist/prover-interface.js +114 -0
  53. package/dist/prover-interface.js.map +1 -0
  54. package/dist/prover.d.ts +163 -0
  55. package/dist/prover.d.ts.map +1 -0
  56. package/dist/prover.js +417 -0
  57. package/dist/prover.js.map +1 -0
  58. package/dist/types.d.ts +410 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +128 -0
  61. package/dist/types.js.map +1 -0
  62. package/package.json +59 -0
  63. package/src/__tests__/hash-chain-proof.test.ts +709 -0
  64. package/src/__tests__/midnight-prover.test.ts +716 -0
  65. package/src/__tests__/policy-compliance.test.ts +567 -0
  66. package/src/__tests__/proof-publication.test.ts +644 -0
  67. package/src/__tests__/selective-disclosure.test.ts +921 -0
  68. package/src/index.ts +260 -0
  69. package/src/linking/cardano-anchor-link.ts +682 -0
  70. package/src/linking/index.ts +58 -0
  71. package/src/linking/proof-publication.ts +557 -0
  72. package/src/midnight/index.ts +73 -0
  73. package/src/midnight/proof-server-client.ts +595 -0
  74. package/src/midnight/public-inputs.ts +590 -0
  75. package/src/midnight/witness-builder.ts +341 -0
  76. package/src/proofs/hash-chain-proof.ts +610 -0
  77. package/src/proofs/index.ts +77 -0
  78. package/src/proofs/policy-compliance-proof.ts +717 -0
  79. package/src/proofs/selective-disclosure.ts +839 -0
  80. package/src/prover-interface.ts +410 -0
  81. package/src/prover.ts +537 -0
  82. package/src/types.ts +551 -0
@@ -0,0 +1,567 @@
1
+ /**
2
+ * @fileoverview Tests for PolicyComplianceProver.
3
+ *
4
+ * Location: packages/midnight-prover/src/__tests__/policy-compliance.test.ts
5
+ *
6
+ * Summary:
7
+ * This file contains unit tests for the PolicyComplianceProver class, testing
8
+ * proof generation, verification, and policy rule evaluation for blocklist,
9
+ * allowlist, regex, and classifier rules.
10
+ */
11
+
12
+ import { describe, it, expect, beforeEach } from "vitest";
13
+ import {
14
+ PolicyComplianceProver,
15
+ createPolicyComplianceProver,
16
+ } from "../proofs/policy-compliance-proof.js";
17
+ import type {
18
+ PolicyInput,
19
+ ContentPolicy,
20
+ PolicyRule,
21
+ } from "../types.js";
22
+
23
+ // =============================================================================
24
+ // TEST FIXTURES
25
+ // =============================================================================
26
+
27
+ /**
28
+ * Generate a mock 64-character hex hash.
29
+ */
30
+ function mockHash(seed: string): string {
31
+ // Simple deterministic hash-like string based on seed
32
+ let hash = "";
33
+ for (let i = 0; i < 64; i++) {
34
+ const charCode = seed.charCodeAt(i % seed.length) || 0;
35
+ hash += ((charCode + i) % 16).toString(16);
36
+ }
37
+ return hash;
38
+ }
39
+
40
+ /**
41
+ * Create a test policy input.
42
+ */
43
+ function createTestPolicyInput(overrides: Partial<PolicyInput> = {}): PolicyInput {
44
+ return {
45
+ promptHash: mockHash("test-prompt"),
46
+ outputHash: mockHash("test-output"),
47
+ policy: {
48
+ id: "test-policy-1",
49
+ version: "1.0.0",
50
+ rules: [],
51
+ },
52
+ cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
53
+ ...overrides,
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Create a blocklist rule.
59
+ */
60
+ function createBlocklistRule(
61
+ blockedHashes: string[] = [],
62
+ target: "prompt" | "output" | "both" = "both"
63
+ ): PolicyRule {
64
+ return {
65
+ type: "blocklist",
66
+ target,
67
+ params: { blockedHashes },
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Create an allowlist rule.
73
+ */
74
+ function createAllowlistRule(
75
+ allowedHashes: string[] = [],
76
+ target: "prompt" | "output" | "both" = "both"
77
+ ): PolicyRule {
78
+ return {
79
+ type: "allowlist",
80
+ target,
81
+ params: { allowedHashes, requireAllMatch: false },
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Create a regex rule.
87
+ */
88
+ function createRegexRule(
89
+ patternHash: string,
90
+ isBlockPattern: boolean = true,
91
+ target: "prompt" | "output" | "both" = "both"
92
+ ): PolicyRule {
93
+ return {
94
+ type: "regex",
95
+ target,
96
+ params: { patternHash, isBlockPattern },
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Create a classifier rule.
102
+ */
103
+ function createClassifierRule(
104
+ classifierId: string = "default",
105
+ threshold: number = 0.5,
106
+ target: "prompt" | "output" | "both" = "both"
107
+ ): PolicyRule {
108
+ return {
109
+ type: "classifier",
110
+ target,
111
+ params: { classifierId, threshold, allowedCategories: [] },
112
+ };
113
+ }
114
+
115
+ // =============================================================================
116
+ // PROVER INSTANTIATION TESTS
117
+ // =============================================================================
118
+
119
+ describe("PolicyComplianceProver instantiation", () => {
120
+ it("creates prover with default options", () => {
121
+ const prover = new PolicyComplianceProver();
122
+ expect(prover).toBeInstanceOf(PolicyComplianceProver);
123
+ });
124
+
125
+ it("creates prover with debug option", () => {
126
+ const prover = new PolicyComplianceProver({ debug: true });
127
+ expect(prover).toBeInstanceOf(PolicyComplianceProver);
128
+ });
129
+
130
+ it("creates prover using factory function", () => {
131
+ const prover = createPolicyComplianceProver();
132
+ expect(prover).toBeInstanceOf(PolicyComplianceProver);
133
+ });
134
+
135
+ it("creates prover with custom evaluators", () => {
136
+ const customEvaluators = new Map<string, (rule: PolicyRule, p: string, o: string) => Promise<{ rule: PolicyRule; passed: boolean; message: string }>>([
137
+ ["custom", async (rule, _p, _o) => ({ rule, passed: true, message: "Custom rule passed" })],
138
+ ]);
139
+
140
+ const prover = new PolicyComplianceProver({ customEvaluators });
141
+ expect(prover).toBeInstanceOf(PolicyComplianceProver);
142
+ });
143
+ });
144
+
145
+ // =============================================================================
146
+ // PROOF GENERATION TESTS
147
+ // =============================================================================
148
+
149
+ describe("PolicyComplianceProver.generateProof", () => {
150
+ let prover: PolicyComplianceProver;
151
+
152
+ beforeEach(() => {
153
+ prover = new PolicyComplianceProver();
154
+ });
155
+
156
+ it("generates proof for policy with no rules (compliant)", async () => {
157
+ const input = createTestPolicyInput();
158
+ const proof = await prover.generateProof(input);
159
+
160
+ expect(proof.proofType).toBe("policy-compliance");
161
+ expect(proof.proofId).toMatch(/^policy-proof-/);
162
+ expect(proof.proof).toBeInstanceOf(Uint8Array);
163
+ expect(proof.proof.length).toBe(65);
164
+ expect(proof.publicInputs.compliant).toBe(true);
165
+ expect(proof.publicInputs.promptHash).toBe(input.promptHash);
166
+ expect(proof.publicInputs.policyId).toBe(input.policy.id);
167
+ expect(proof.publicInputs.policyVersion).toBe(input.policy.version);
168
+ expect(proof.publicInputs.cardanoAnchorTxHash).toBe(input.cardanoAnchorTxHash);
169
+ expect(proof.provingTimeMs).toBeGreaterThanOrEqual(0);
170
+ expect(proof.proofSizeBytes).toBe(65);
171
+ expect(proof.createdAt).toBeDefined();
172
+ });
173
+
174
+ it("generates proof with blocklist rule (compliant)", async () => {
175
+ const input = createTestPolicyInput({
176
+ policy: {
177
+ id: "blocklist-policy",
178
+ version: "1.0.0",
179
+ rules: [createBlocklistRule(["blocked-hash-1", "blocked-hash-2"])],
180
+ },
181
+ });
182
+
183
+ const proof = await prover.generateProof(input);
184
+ expect(proof.publicInputs.compliant).toBe(true);
185
+ });
186
+
187
+ it("generates proof with blocklist rule (non-compliant)", async () => {
188
+ const promptHash = mockHash("test-prompt");
189
+ const input = createTestPolicyInput({
190
+ promptHash,
191
+ policy: {
192
+ id: "blocklist-policy",
193
+ version: "1.0.0",
194
+ rules: [createBlocklistRule([promptHash])], // Block the actual prompt hash
195
+ },
196
+ });
197
+
198
+ const proof = await prover.generateProof(input);
199
+ expect(proof.publicInputs.compliant).toBe(false);
200
+ });
201
+
202
+ it("generates proof with allowlist rule (empty list is permissive)", async () => {
203
+ const input = createTestPolicyInput({
204
+ policy: {
205
+ id: "allowlist-policy",
206
+ version: "1.0.0",
207
+ rules: [createAllowlistRule([])],
208
+ },
209
+ });
210
+
211
+ const proof = await prover.generateProof(input);
212
+ expect(proof.publicInputs.compliant).toBe(true);
213
+ });
214
+
215
+ it("generates proof with regex rule (compliant - no match)", async () => {
216
+ const input = createTestPolicyInput({
217
+ policy: {
218
+ id: "regex-policy",
219
+ version: "1.0.0",
220
+ rules: [createRegexRule("different-pattern", true)],
221
+ },
222
+ });
223
+
224
+ const proof = await prover.generateProof(input);
225
+ expect(proof.publicInputs.compliant).toBe(true);
226
+ });
227
+
228
+ it("generates proof with classifier rule", async () => {
229
+ const input = createTestPolicyInput({
230
+ policy: {
231
+ id: "classifier-policy",
232
+ version: "1.0.0",
233
+ rules: [createClassifierRule("safety-classifier", 0.3)],
234
+ },
235
+ });
236
+
237
+ const proof = await prover.generateProof(input);
238
+ // Classifier result depends on hash-based simulation
239
+ expect(typeof proof.publicInputs.compliant).toBe("boolean");
240
+ });
241
+
242
+ it("generates proof with multiple rules", async () => {
243
+ const input = createTestPolicyInput({
244
+ policy: {
245
+ id: "multi-rule-policy",
246
+ version: "1.0.0",
247
+ rules: [
248
+ createBlocklistRule(["bad-hash-1"]),
249
+ createAllowlistRule([]),
250
+ createClassifierRule("content-filter", 0.2),
251
+ ],
252
+ },
253
+ });
254
+
255
+ const proof = await prover.generateProof(input);
256
+ expect(proof.proofType).toBe("policy-compliance");
257
+ expect(typeof proof.publicInputs.compliant).toBe("boolean");
258
+ });
259
+
260
+ it("requires all rules to pass for compliance", async () => {
261
+ const promptHash = mockHash("test-prompt");
262
+ const input = createTestPolicyInput({
263
+ promptHash,
264
+ policy: {
265
+ id: "strict-policy",
266
+ version: "1.0.0",
267
+ rules: [
268
+ createBlocklistRule([]), // Passes
269
+ createBlocklistRule([promptHash]), // Fails
270
+ ],
271
+ },
272
+ });
273
+
274
+ const proof = await prover.generateProof(input);
275
+ expect(proof.publicInputs.compliant).toBe(false);
276
+ });
277
+ });
278
+
279
+ // =============================================================================
280
+ // INPUT VALIDATION TESTS
281
+ // =============================================================================
282
+
283
+ describe("PolicyComplianceProver input validation", () => {
284
+ let prover: PolicyComplianceProver;
285
+
286
+ beforeEach(() => {
287
+ prover = new PolicyComplianceProver();
288
+ });
289
+
290
+ it("rejects invalid prompt hash (too short)", async () => {
291
+ const input = createTestPolicyInput({ promptHash: "abc123" });
292
+ await expect(prover.generateProof(input)).rejects.toThrow(/Invalid prompt hash/);
293
+ });
294
+
295
+ it("rejects invalid output hash (too short)", async () => {
296
+ const input = createTestPolicyInput({ outputHash: "def456" });
297
+ await expect(prover.generateProof(input)).rejects.toThrow(/Invalid output hash/);
298
+ });
299
+
300
+ it("rejects missing policy ID", async () => {
301
+ const input = createTestPolicyInput({
302
+ policy: { id: "", version: "1.0.0", rules: [] },
303
+ });
304
+ await expect(prover.generateProof(input)).rejects.toThrow(/Policy ID is required/);
305
+ });
306
+
307
+ it("rejects missing policy version", async () => {
308
+ const input = createTestPolicyInput({
309
+ policy: { id: "test", version: "", rules: [] },
310
+ });
311
+ await expect(prover.generateProof(input)).rejects.toThrow(/Policy version is required/);
312
+ });
313
+
314
+ it("rejects missing Cardano anchor hash", async () => {
315
+ const input = createTestPolicyInput({ cardanoAnchorTxHash: "" });
316
+ await expect(prover.generateProof(input)).rejects.toThrow(/Cardano anchor transaction hash is required/);
317
+ });
318
+
319
+ it("rejects unknown rule type", async () => {
320
+ const input = createTestPolicyInput({
321
+ policy: {
322
+ id: "test",
323
+ version: "1.0.0",
324
+ rules: [{ type: "unknown" as "blocklist", target: "both", params: {} }],
325
+ },
326
+ });
327
+ await expect(prover.generateProof(input)).rejects.toThrow(/Unknown rule type/);
328
+ });
329
+ });
330
+
331
+ // =============================================================================
332
+ // PROOF VERIFICATION TESTS
333
+ // =============================================================================
334
+
335
+ describe("PolicyComplianceProver.verifyProof", () => {
336
+ let prover: PolicyComplianceProver;
337
+
338
+ beforeEach(() => {
339
+ prover = new PolicyComplianceProver();
340
+ });
341
+
342
+ it("verifies a valid proof", async () => {
343
+ const input = createTestPolicyInput();
344
+ const proof = await prover.generateProof(input);
345
+ const isValid = await prover.verifyProof(proof);
346
+ expect(isValid).toBe(true);
347
+ });
348
+
349
+ it("verifies compliant proof", async () => {
350
+ const input = createTestPolicyInput({
351
+ policy: {
352
+ id: "safe-policy",
353
+ version: "1.0.0",
354
+ rules: [createBlocklistRule([])],
355
+ },
356
+ });
357
+
358
+ const proof = await prover.generateProof(input);
359
+ expect(proof.publicInputs.compliant).toBe(true);
360
+ expect(await prover.verifyProof(proof)).toBe(true);
361
+ });
362
+
363
+ it("verifies non-compliant proof", async () => {
364
+ const promptHash = mockHash("test-prompt");
365
+ const input = createTestPolicyInput({
366
+ promptHash,
367
+ policy: {
368
+ id: "strict-policy",
369
+ version: "1.0.0",
370
+ rules: [createBlocklistRule([promptHash])],
371
+ },
372
+ });
373
+
374
+ const proof = await prover.generateProof(input);
375
+ expect(proof.publicInputs.compliant).toBe(false);
376
+ expect(await prover.verifyProof(proof)).toBe(true);
377
+ });
378
+
379
+ it("rejects proof with wrong type", async () => {
380
+ const input = createTestPolicyInput();
381
+ const proof = await prover.generateProof(input);
382
+ (proof as { proofType: string }).proofType = "hash-chain";
383
+ expect(await prover.verifyProof(proof)).toBe(false);
384
+ });
385
+
386
+ it("rejects proof with empty proof bytes", async () => {
387
+ const input = createTestPolicyInput();
388
+ const proof = await prover.generateProof(input);
389
+ proof.proof = new Uint8Array(0);
390
+ expect(await prover.verifyProof(proof)).toBe(false);
391
+ });
392
+
393
+ it("rejects proof with tampered proof ID", async () => {
394
+ const input = createTestPolicyInput();
395
+ const proof = await prover.generateProof(input);
396
+ proof.proofId = "tampered-proof-id";
397
+ expect(await prover.verifyProof(proof)).toBe(false);
398
+ });
399
+
400
+ it("rejects proof with invalid prompt hash", async () => {
401
+ const input = createTestPolicyInput();
402
+ const proof = await prover.generateProof(input);
403
+ proof.publicInputs.promptHash = "short";
404
+ expect(await prover.verifyProof(proof)).toBe(false);
405
+ });
406
+
407
+ it("rejects proof with wrong version byte", async () => {
408
+ const input = createTestPolicyInput();
409
+ const proof = await prover.generateProof(input);
410
+ proof.proof[0] = 0x99; // Wrong version
411
+ // Still passes because we regenerate proof ID check
412
+ const isValid = await prover.verifyProof(proof);
413
+ expect(isValid).toBe(false);
414
+ });
415
+ });
416
+
417
+ // =============================================================================
418
+ // POLICY EVALUATION TESTS
419
+ // =============================================================================
420
+
421
+ describe("PolicyComplianceProver.evaluatePolicy", () => {
422
+ let prover: PolicyComplianceProver;
423
+
424
+ beforeEach(() => {
425
+ prover = new PolicyComplianceProver();
426
+ });
427
+
428
+ it("evaluates empty policy as compliant", async () => {
429
+ const policy: ContentPolicy = { id: "empty", version: "1.0.0", rules: [] };
430
+ const result = await prover.evaluatePolicy(policy, mockHash("prompt"), mockHash("output"));
431
+
432
+ expect(result.compliant).toBe(true);
433
+ expect(result.ruleResults).toHaveLength(0);
434
+ expect(result.evaluationTimeMs).toBeGreaterThanOrEqual(0);
435
+ });
436
+
437
+ it("evaluates blocklist rule targeting prompt only", async () => {
438
+ const promptHash = mockHash("blocked-prompt");
439
+ const policy: ContentPolicy = {
440
+ id: "prompt-blocklist",
441
+ version: "1.0.0",
442
+ rules: [createBlocklistRule([promptHash], "prompt")],
443
+ };
444
+
445
+ const result = await prover.evaluatePolicy(policy, promptHash, mockHash("safe-output"));
446
+ expect(result.compliant).toBe(false);
447
+ expect(result.ruleResults[0]?.passed).toBe(false);
448
+ });
449
+
450
+ it("evaluates blocklist rule targeting output only", async () => {
451
+ const outputHash = mockHash("blocked-output");
452
+ const policy: ContentPolicy = {
453
+ id: "output-blocklist",
454
+ version: "1.0.0",
455
+ rules: [createBlocklistRule([outputHash], "output")],
456
+ };
457
+
458
+ const result = await prover.evaluatePolicy(policy, mockHash("safe-prompt"), outputHash);
459
+ expect(result.compliant).toBe(false);
460
+ expect(result.ruleResults[0]?.passed).toBe(false);
461
+ });
462
+
463
+ it("evaluates multiple rules returning all results", async () => {
464
+ const policy: ContentPolicy = {
465
+ id: "multi-rule",
466
+ version: "1.0.0",
467
+ rules: [
468
+ createBlocklistRule([]),
469
+ createAllowlistRule([]),
470
+ createClassifierRule("test", 0.2),
471
+ ],
472
+ };
473
+
474
+ const result = await prover.evaluatePolicy(policy, mockHash("prompt"), mockHash("output"));
475
+ expect(result.ruleResults).toHaveLength(3);
476
+ expect(result.ruleResults[0]?.rule.type).toBe("blocklist");
477
+ expect(result.ruleResults[1]?.rule.type).toBe("allowlist");
478
+ expect(result.ruleResults[2]?.rule.type).toBe("classifier");
479
+ });
480
+ });
481
+
482
+ // =============================================================================
483
+ // CUSTOM EVALUATOR TESTS
484
+ // =============================================================================
485
+
486
+ describe("PolicyComplianceProver custom evaluators", () => {
487
+ it("uses registered custom evaluator", async () => {
488
+ const prover = new PolicyComplianceProver();
489
+
490
+ prover.registerEvaluator("custom-check", async (rule, _p, _o) => ({
491
+ rule,
492
+ passed: true,
493
+ message: "Custom check passed",
494
+ }));
495
+
496
+ const input = createTestPolicyInput({
497
+ policy: {
498
+ id: "custom-policy",
499
+ version: "1.0.0",
500
+ rules: [{ type: "custom-check" as "blocklist", target: "both", params: {} }],
501
+ },
502
+ });
503
+
504
+ const proof = await prover.generateProof(input);
505
+ expect(proof.publicInputs.compliant).toBe(true);
506
+ });
507
+
508
+ it("custom evaluator can fail compliance", async () => {
509
+ const prover = new PolicyComplianceProver();
510
+
511
+ prover.registerEvaluator("always-fail", async (rule, _p, _o) => ({
512
+ rule,
513
+ passed: false,
514
+ message: "Always fails",
515
+ }));
516
+
517
+ const input = createTestPolicyInput({
518
+ policy: {
519
+ id: "fail-policy",
520
+ version: "1.0.0",
521
+ rules: [{ type: "always-fail" as "blocklist", target: "both", params: {} }],
522
+ },
523
+ });
524
+
525
+ const proof = await prover.generateProof(input);
526
+ expect(proof.publicInputs.compliant).toBe(false);
527
+ });
528
+ });
529
+
530
+ // =============================================================================
531
+ // PROOF METRICS TESTS
532
+ // =============================================================================
533
+
534
+ describe("PolicyComplianceProver proof metrics", () => {
535
+ let prover: PolicyComplianceProver;
536
+
537
+ beforeEach(() => {
538
+ prover = new PolicyComplianceProver();
539
+ });
540
+
541
+ it("includes proving time in proof", async () => {
542
+ const input = createTestPolicyInput();
543
+ const proof = await prover.generateProof(input);
544
+
545
+ expect(proof.provingTimeMs).toBeGreaterThanOrEqual(0);
546
+ expect(proof.provingTimeMs).toBeLessThan(10000); // Should be fast for mock
547
+ });
548
+
549
+ it("includes proof size in proof", async () => {
550
+ const input = createTestPolicyInput();
551
+ const proof = await prover.generateProof(input);
552
+
553
+ expect(proof.proofSizeBytes).toBe(proof.proof.length);
554
+ expect(proof.proofSizeBytes).toBe(65); // Version + commitment + witness hash
555
+ });
556
+
557
+ it("includes creation timestamp in proof", async () => {
558
+ const beforeTime = new Date().toISOString();
559
+ const input = createTestPolicyInput();
560
+ const proof = await prover.generateProof(input);
561
+ const afterTime = new Date().toISOString();
562
+
563
+ expect(proof.createdAt).toBeDefined();
564
+ expect(proof.createdAt >= beforeTime).toBe(true);
565
+ expect(proof.createdAt <= afterTime).toBe(true);
566
+ });
567
+ });