@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.
- package/LICENSE +21 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/linking/cardano-anchor-link.d.ts +250 -0
- package/dist/linking/cardano-anchor-link.d.ts.map +1 -0
- package/dist/linking/cardano-anchor-link.js +447 -0
- package/dist/linking/cardano-anchor-link.js.map +1 -0
- package/dist/linking/index.d.ts +33 -0
- package/dist/linking/index.d.ts.map +1 -0
- package/dist/linking/index.js +31 -0
- package/dist/linking/index.js.map +1 -0
- package/dist/linking/proof-publication.d.ts +217 -0
- package/dist/linking/proof-publication.d.ts.map +1 -0
- package/dist/linking/proof-publication.js +385 -0
- package/dist/linking/proof-publication.js.map +1 -0
- package/dist/midnight/index.d.ts +30 -0
- package/dist/midnight/index.d.ts.map +1 -0
- package/dist/midnight/index.js +27 -0
- package/dist/midnight/index.js.map +1 -0
- package/dist/midnight/proof-server-client.d.ts +236 -0
- package/dist/midnight/proof-server-client.d.ts.map +1 -0
- package/dist/midnight/proof-server-client.js +422 -0
- package/dist/midnight/proof-server-client.js.map +1 -0
- package/dist/midnight/public-inputs.d.ts +134 -0
- package/dist/midnight/public-inputs.d.ts.map +1 -0
- package/dist/midnight/public-inputs.js +338 -0
- package/dist/midnight/public-inputs.js.map +1 -0
- package/dist/midnight/witness-builder.d.ts +119 -0
- package/dist/midnight/witness-builder.d.ts.map +1 -0
- package/dist/midnight/witness-builder.js +238 -0
- package/dist/midnight/witness-builder.js.map +1 -0
- package/dist/proofs/hash-chain-proof.d.ts +171 -0
- package/dist/proofs/hash-chain-proof.d.ts.map +1 -0
- package/dist/proofs/hash-chain-proof.js +437 -0
- package/dist/proofs/hash-chain-proof.js.map +1 -0
- package/dist/proofs/index.d.ts +35 -0
- package/dist/proofs/index.d.ts.map +1 -0
- package/dist/proofs/index.js +34 -0
- package/dist/proofs/index.js.map +1 -0
- package/dist/proofs/policy-compliance-proof.d.ts +165 -0
- package/dist/proofs/policy-compliance-proof.d.ts.map +1 -0
- package/dist/proofs/policy-compliance-proof.js +514 -0
- package/dist/proofs/policy-compliance-proof.js.map +1 -0
- package/dist/proofs/selective-disclosure.d.ts +213 -0
- package/dist/proofs/selective-disclosure.d.ts.map +1 -0
- package/dist/proofs/selective-disclosure.js +629 -0
- package/dist/proofs/selective-disclosure.js.map +1 -0
- package/dist/prover-interface.d.ts +288 -0
- package/dist/prover-interface.d.ts.map +1 -0
- package/dist/prover-interface.js +114 -0
- package/dist/prover-interface.js.map +1 -0
- package/dist/prover.d.ts +163 -0
- package/dist/prover.d.ts.map +1 -0
- package/dist/prover.js +417 -0
- package/dist/prover.js.map +1 -0
- package/dist/types.d.ts +410 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +128 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -0
- package/src/__tests__/hash-chain-proof.test.ts +709 -0
- package/src/__tests__/midnight-prover.test.ts +716 -0
- package/src/__tests__/policy-compliance.test.ts +567 -0
- package/src/__tests__/proof-publication.test.ts +644 -0
- package/src/__tests__/selective-disclosure.test.ts +921 -0
- package/src/index.ts +260 -0
- package/src/linking/cardano-anchor-link.ts +682 -0
- package/src/linking/index.ts +58 -0
- package/src/linking/proof-publication.ts +557 -0
- package/src/midnight/index.ts +73 -0
- package/src/midnight/proof-server-client.ts +595 -0
- package/src/midnight/public-inputs.ts +590 -0
- package/src/midnight/witness-builder.ts +341 -0
- package/src/proofs/hash-chain-proof.ts +610 -0
- package/src/proofs/index.ts +77 -0
- package/src/proofs/policy-compliance-proof.ts +717 -0
- package/src/proofs/selective-disclosure.ts +839 -0
- package/src/prover-interface.ts +410 -0
- package/src/prover.ts +537 -0
- package/src/types.ts +551 -0
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Integration tests for the DefaultMidnightProver.
|
|
3
|
+
*
|
|
4
|
+
* Location: packages/midnight-prover/src/__tests__/midnight-prover.test.ts
|
|
5
|
+
*
|
|
6
|
+
* Summary:
|
|
7
|
+
* Integration tests for the DefaultMidnightProver class which coordinates all
|
|
8
|
+
* proof types and handles the full lifecycle of proof generation and publication.
|
|
9
|
+
* Tests cover connection management, proof generation, publication, and verification.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* Run with: pnpm test -- packages/midnight-prover
|
|
13
|
+
*
|
|
14
|
+
* Related files:
|
|
15
|
+
* - prover.ts: DefaultMidnightProver implementation
|
|
16
|
+
* - prover-interface.ts: MidnightProver interface definition
|
|
17
|
+
* - proofs/: Individual prover implementations
|
|
18
|
+
* - linking/: Publication and cross-chain linking
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
DefaultMidnightProver,
|
|
25
|
+
createMidnightProver,
|
|
26
|
+
} from "../prover.js";
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
proverRegistry,
|
|
30
|
+
} from "../prover-interface.js";
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
HashChainProver,
|
|
34
|
+
} from "../proofs/hash-chain-proof.js";
|
|
35
|
+
|
|
36
|
+
import {
|
|
37
|
+
PolicyComplianceProver,
|
|
38
|
+
} from "../proofs/policy-compliance-proof.js";
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
SelectiveDisclosureProver,
|
|
42
|
+
} from "../proofs/selective-disclosure.js";
|
|
43
|
+
|
|
44
|
+
import {
|
|
45
|
+
CardanoAnchorLinker,
|
|
46
|
+
} from "../linking/cardano-anchor-link.js";
|
|
47
|
+
|
|
48
|
+
import {
|
|
49
|
+
buildHashChainWitness,
|
|
50
|
+
} from "../midnight/witness-builder.js";
|
|
51
|
+
|
|
52
|
+
import {
|
|
53
|
+
MidnightProverError,
|
|
54
|
+
MidnightProverException,
|
|
55
|
+
isHashChainProof,
|
|
56
|
+
isPolicyProof,
|
|
57
|
+
isDisclosureProof,
|
|
58
|
+
type HashChainInput,
|
|
59
|
+
type PolicyInput,
|
|
60
|
+
type DisclosureInput,
|
|
61
|
+
type ProofServerConfig,
|
|
62
|
+
} from "../types.js";
|
|
63
|
+
|
|
64
|
+
import type {
|
|
65
|
+
TraceEvent,
|
|
66
|
+
TraceBundle,
|
|
67
|
+
TraceSpan,
|
|
68
|
+
CommandEvent,
|
|
69
|
+
OutputEvent,
|
|
70
|
+
} from "@fluxpointstudios/orynq-sdk-process-trace";
|
|
71
|
+
|
|
72
|
+
// =============================================================================
|
|
73
|
+
// TEST HELPERS
|
|
74
|
+
// =============================================================================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create a valid 64-character hex hash.
|
|
78
|
+
*/
|
|
79
|
+
function createMockHash(seed: string): string {
|
|
80
|
+
const hex = "0123456789abcdef";
|
|
81
|
+
let result = "";
|
|
82
|
+
for (let i = 0; i < 64; i++) {
|
|
83
|
+
result += hex[(seed.charCodeAt(i % seed.length) + i) % 16];
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a mock trace event.
|
|
90
|
+
*/
|
|
91
|
+
function createMockEvent(seq: number, kind: string = "command"): TraceEvent {
|
|
92
|
+
const baseEvent = {
|
|
93
|
+
id: `event-${seq}-${Date.now()}`,
|
|
94
|
+
seq,
|
|
95
|
+
timestamp: new Date().toISOString(),
|
|
96
|
+
visibility: "public" as const,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
if (kind === "command") {
|
|
100
|
+
return {
|
|
101
|
+
...baseEvent,
|
|
102
|
+
kind: "command",
|
|
103
|
+
command: `test-command-${seq}`,
|
|
104
|
+
args: [`arg-${seq}`],
|
|
105
|
+
} as CommandEvent;
|
|
106
|
+
} else {
|
|
107
|
+
return {
|
|
108
|
+
...baseEvent,
|
|
109
|
+
kind: "output",
|
|
110
|
+
stream: "stdout",
|
|
111
|
+
content: `output content ${seq}`,
|
|
112
|
+
} as OutputEvent;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create an array of mock events.
|
|
118
|
+
*/
|
|
119
|
+
function createMockEvents(count: number): TraceEvent[] {
|
|
120
|
+
const events: TraceEvent[] = [];
|
|
121
|
+
for (let i = 1; i <= count; i++) {
|
|
122
|
+
events.push(createMockEvent(i, i % 2 === 0 ? "output" : "command"));
|
|
123
|
+
}
|
|
124
|
+
return events;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create a mock trace span.
|
|
129
|
+
*/
|
|
130
|
+
function createMockSpan(id: string, seq: number, eventIds: string[]): TraceSpan {
|
|
131
|
+
return {
|
|
132
|
+
id,
|
|
133
|
+
spanSeq: seq,
|
|
134
|
+
name: `span-${seq}`,
|
|
135
|
+
status: "completed",
|
|
136
|
+
visibility: "public",
|
|
137
|
+
startedAt: new Date().toISOString(),
|
|
138
|
+
endedAt: new Date().toISOString(),
|
|
139
|
+
durationMs: 100,
|
|
140
|
+
eventIds,
|
|
141
|
+
childSpanIds: [],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Create a mock trace bundle.
|
|
147
|
+
*/
|
|
148
|
+
function createMockTraceBundle(): TraceBundle {
|
|
149
|
+
const events = createMockEvents(5);
|
|
150
|
+
const span1 = createMockSpan("span-1", 1, [events[0]!.id, events[1]!.id]);
|
|
151
|
+
const span2 = createMockSpan("span-2", 2, [events[2]!.id, events[3]!.id, events[4]!.id]);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
publicRun: {
|
|
155
|
+
sessionId: "session-123",
|
|
156
|
+
events: events.filter((_, i) => i < 2),
|
|
157
|
+
spans: [span1],
|
|
158
|
+
},
|
|
159
|
+
privateRun: {
|
|
160
|
+
sessionId: "session-123",
|
|
161
|
+
events,
|
|
162
|
+
spans: [span1, span2],
|
|
163
|
+
},
|
|
164
|
+
rootHash: createMockHash("root"),
|
|
165
|
+
merkleRoot: createMockHash("merkle"),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Create a mock proof server config.
|
|
171
|
+
*/
|
|
172
|
+
function createMockConfig(): ProofServerConfig {
|
|
173
|
+
return {
|
|
174
|
+
proofServerUrl: "https://mock.proof.server",
|
|
175
|
+
apiKey: "test-api-key",
|
|
176
|
+
timeout: 30000,
|
|
177
|
+
retries: 3,
|
|
178
|
+
circuitCacheDir: undefined,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// =============================================================================
|
|
183
|
+
// DEFAULT MIDNIGHT PROVER TESTS
|
|
184
|
+
// =============================================================================
|
|
185
|
+
|
|
186
|
+
describe("DefaultMidnightProver", () => {
|
|
187
|
+
let prover: DefaultMidnightProver;
|
|
188
|
+
const config = createMockConfig();
|
|
189
|
+
|
|
190
|
+
beforeEach(async () => {
|
|
191
|
+
prover = new DefaultMidnightProver({ debug: false });
|
|
192
|
+
await prover.connect(config);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
afterEach(async () => {
|
|
196
|
+
await prover.disconnect();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("constructor", () => {
|
|
200
|
+
it("should create a prover with default options", () => {
|
|
201
|
+
const defaultProver = new DefaultMidnightProver();
|
|
202
|
+
expect(defaultProver).toBeInstanceOf(DefaultMidnightProver);
|
|
203
|
+
expect(defaultProver.isConnected()).toBe(false);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("should create a prover with custom options", () => {
|
|
207
|
+
const customProver = new DefaultMidnightProver({
|
|
208
|
+
debug: true,
|
|
209
|
+
circuitsDir: "./custom-circuits",
|
|
210
|
+
enableInference: false,
|
|
211
|
+
});
|
|
212
|
+
expect(customProver).toBeInstanceOf(DefaultMidnightProver);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("connection management", () => {
|
|
217
|
+
it("should connect and disconnect successfully", async () => {
|
|
218
|
+
const newProver = new DefaultMidnightProver();
|
|
219
|
+
|
|
220
|
+
expect(newProver.isConnected()).toBe(false);
|
|
221
|
+
await newProver.connect(config);
|
|
222
|
+
expect(newProver.isConnected()).toBe(true);
|
|
223
|
+
expect(newProver.getConfig()).toBeDefined();
|
|
224
|
+
|
|
225
|
+
await newProver.disconnect();
|
|
226
|
+
expect(newProver.isConnected()).toBe(false);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("should throw for operations when not connected", async () => {
|
|
230
|
+
const disconnectedProver = new DefaultMidnightProver();
|
|
231
|
+
|
|
232
|
+
await expect(
|
|
233
|
+
disconnectedProver.proveHashChain({
|
|
234
|
+
events: createMockEvents(3),
|
|
235
|
+
genesisHash: createMockHash("genesis"),
|
|
236
|
+
expectedRootHash: createMockHash("root"),
|
|
237
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
238
|
+
})
|
|
239
|
+
).rejects.toThrow();
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("proveHashChain", () => {
|
|
244
|
+
it("should generate a hash-chain proof", async () => {
|
|
245
|
+
const events = createMockEvents(5);
|
|
246
|
+
const genesisHash = await prover.getGenesisHash();
|
|
247
|
+
const witness = await buildHashChainWitness(events, genesisHash);
|
|
248
|
+
|
|
249
|
+
const input: HashChainInput = {
|
|
250
|
+
events,
|
|
251
|
+
genesisHash,
|
|
252
|
+
expectedRootHash: witness.computedRollingHash,
|
|
253
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const proof = await prover.proveHashChain(input);
|
|
257
|
+
|
|
258
|
+
expect(proof).toBeDefined();
|
|
259
|
+
expect(isHashChainProof(proof)).toBe(true);
|
|
260
|
+
expect(proof.proofId).toBeDefined();
|
|
261
|
+
expect(proof.publicInputs.rootHash).toBe(witness.computedRollingHash.toLowerCase());
|
|
262
|
+
expect(proof.publicInputs.eventCount).toBe(5);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("should throw for invalid input", async () => {
|
|
266
|
+
const input: HashChainInput = {
|
|
267
|
+
events: [], // Empty events
|
|
268
|
+
genesisHash: createMockHash("genesis"),
|
|
269
|
+
expectedRootHash: createMockHash("root"),
|
|
270
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
await expect(prover.proveHashChain(input)).rejects.toThrow(MidnightProverException);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe("provePolicyCompliance", () => {
|
|
278
|
+
it("should generate a policy compliance proof", async () => {
|
|
279
|
+
const input: PolicyInput = {
|
|
280
|
+
promptHash: createMockHash("prompt"),
|
|
281
|
+
outputHash: createMockHash("output"),
|
|
282
|
+
policy: {
|
|
283
|
+
id: "test-policy",
|
|
284
|
+
version: "1.0.0",
|
|
285
|
+
rules: [
|
|
286
|
+
{
|
|
287
|
+
type: "blocklist",
|
|
288
|
+
target: "both",
|
|
289
|
+
params: { blockedHashes: [] },
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
},
|
|
293
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const proof = await prover.provePolicyCompliance(input);
|
|
297
|
+
|
|
298
|
+
expect(proof).toBeDefined();
|
|
299
|
+
expect(isPolicyProof(proof)).toBe(true);
|
|
300
|
+
expect(proof.publicInputs.policyId).toBe("test-policy");
|
|
301
|
+
expect(proof.publicInputs.compliant).toBe(true);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("should detect non-compliance", async () => {
|
|
305
|
+
const blockedHash = createMockHash("blocked");
|
|
306
|
+
|
|
307
|
+
const input: PolicyInput = {
|
|
308
|
+
promptHash: blockedHash,
|
|
309
|
+
outputHash: createMockHash("output"),
|
|
310
|
+
policy: {
|
|
311
|
+
id: "strict-policy",
|
|
312
|
+
version: "1.0.0",
|
|
313
|
+
rules: [
|
|
314
|
+
{
|
|
315
|
+
type: "blocklist",
|
|
316
|
+
target: "prompt",
|
|
317
|
+
params: { blockedHashes: [blockedHash] },
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const proof = await prover.provePolicyCompliance(input);
|
|
325
|
+
|
|
326
|
+
expect(proof.publicInputs.compliant).toBe(false);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe("proveSelectiveDisclosure", () => {
|
|
331
|
+
it("should generate a selective disclosure proof", async () => {
|
|
332
|
+
const bundle = createMockTraceBundle();
|
|
333
|
+
|
|
334
|
+
const input: DisclosureInput = {
|
|
335
|
+
bundle,
|
|
336
|
+
spanId: "span-1",
|
|
337
|
+
merkleRoot: bundle.merkleRoot ?? createMockHash("merkle"),
|
|
338
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// Note: The merkle root in the input must match what's computed from the spans
|
|
342
|
+
// For this test, we'll generate the proof but it may fail verification
|
|
343
|
+
// since we're using a mock merkle root
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
const proof = await prover.proveSelectiveDisclosure(input);
|
|
347
|
+
expect(proof).toBeDefined();
|
|
348
|
+
expect(isDisclosureProof(proof)).toBe(true);
|
|
349
|
+
expect(proof.disclosedSpan).toBeDefined();
|
|
350
|
+
} catch (error) {
|
|
351
|
+
// Expected: hash mismatch since we're using mock merkle root
|
|
352
|
+
expect(error).toBeInstanceOf(MidnightProverException);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("should throw for non-existent span", async () => {
|
|
357
|
+
const bundle = createMockTraceBundle();
|
|
358
|
+
|
|
359
|
+
const input: DisclosureInput = {
|
|
360
|
+
bundle,
|
|
361
|
+
spanId: "non-existent-span",
|
|
362
|
+
merkleRoot: createMockHash("merkle"),
|
|
363
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
await expect(prover.proveSelectiveDisclosure(input)).rejects.toThrow(
|
|
367
|
+
MidnightProverException
|
|
368
|
+
);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe("proveAttestation", () => {
|
|
373
|
+
it("should throw not implemented error", async () => {
|
|
374
|
+
const input = {
|
|
375
|
+
attestation: {} as never,
|
|
376
|
+
policy: {} as never,
|
|
377
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
await expect(prover.proveAttestation(input)).rejects.toThrow(
|
|
381
|
+
/not yet implemented/i
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe("proveInference", () => {
|
|
387
|
+
it("should throw error when inference is disabled", async () => {
|
|
388
|
+
const input = {
|
|
389
|
+
modelId: "test-model",
|
|
390
|
+
modelWeightDigest: createMockHash("weights"),
|
|
391
|
+
inputTokens: [1, 2, 3],
|
|
392
|
+
outputTokens: [4, 5, 6],
|
|
393
|
+
params: {
|
|
394
|
+
temperature: 0.7,
|
|
395
|
+
topP: undefined,
|
|
396
|
+
topK: undefined,
|
|
397
|
+
maxTokens: 100,
|
|
398
|
+
stopStrings: undefined,
|
|
399
|
+
frequencyPenalty: undefined,
|
|
400
|
+
presencePenalty: undefined,
|
|
401
|
+
},
|
|
402
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
expect(() => prover.proveInference(input)).toThrow(/disabled/i);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it("should throw not implemented even when enabled", async () => {
|
|
409
|
+
const enabledProver = new DefaultMidnightProver({ enableInference: true });
|
|
410
|
+
await enabledProver.connect(config);
|
|
411
|
+
|
|
412
|
+
const input = {
|
|
413
|
+
modelId: "test-model",
|
|
414
|
+
modelWeightDigest: createMockHash("weights"),
|
|
415
|
+
inputTokens: [1, 2, 3],
|
|
416
|
+
outputTokens: [4, 5, 6],
|
|
417
|
+
params: {
|
|
418
|
+
temperature: 0.7,
|
|
419
|
+
topP: undefined,
|
|
420
|
+
topK: undefined,
|
|
421
|
+
maxTokens: 100,
|
|
422
|
+
stopStrings: undefined,
|
|
423
|
+
frequencyPenalty: undefined,
|
|
424
|
+
presencePenalty: undefined,
|
|
425
|
+
},
|
|
426
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
expect(() => enabledProver.proveInference(input)).toThrow(/not yet implemented/i);
|
|
430
|
+
|
|
431
|
+
await enabledProver.disconnect();
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
describe("publish", () => {
|
|
436
|
+
it("should publish a proof to the network", async () => {
|
|
437
|
+
const events = createMockEvents(3);
|
|
438
|
+
const genesisHash = await prover.getGenesisHash();
|
|
439
|
+
const witness = await buildHashChainWitness(events, genesisHash);
|
|
440
|
+
|
|
441
|
+
const hashChainProof = await prover.proveHashChain({
|
|
442
|
+
events,
|
|
443
|
+
genesisHash,
|
|
444
|
+
expectedRootHash: witness.computedRollingHash,
|
|
445
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const result = await prover.publish(hashChainProof);
|
|
449
|
+
|
|
450
|
+
expect(result).toBeDefined();
|
|
451
|
+
expect(result.midnightTxHash).toMatch(/^[0-9a-f]{64}$/);
|
|
452
|
+
expect(result.proofId).toBe(hashChainProof.proofId);
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
describe("verify", () => {
|
|
457
|
+
it("should verify a valid proof", async () => {
|
|
458
|
+
const events = createMockEvents(3);
|
|
459
|
+
const genesisHash = await prover.getGenesisHash();
|
|
460
|
+
const witness = await buildHashChainWitness(events, genesisHash);
|
|
461
|
+
|
|
462
|
+
const proof = await prover.proveHashChain({
|
|
463
|
+
events,
|
|
464
|
+
genesisHash,
|
|
465
|
+
expectedRootHash: witness.computedRollingHash,
|
|
466
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const result = await prover.verify(proof);
|
|
470
|
+
|
|
471
|
+
expect(result.valid).toBe(true);
|
|
472
|
+
expect(result.proofType).toBe("hash-chain");
|
|
473
|
+
expect(result.errors).toHaveLength(0);
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
describe("fetchProof", () => {
|
|
478
|
+
it("should return published proof from cache", async () => {
|
|
479
|
+
const events = createMockEvents(3);
|
|
480
|
+
const genesisHash = await prover.getGenesisHash();
|
|
481
|
+
const witness = await buildHashChainWitness(events, genesisHash);
|
|
482
|
+
|
|
483
|
+
const proof = await prover.proveHashChain({
|
|
484
|
+
events,
|
|
485
|
+
genesisHash,
|
|
486
|
+
expectedRootHash: witness.computedRollingHash,
|
|
487
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
await prover.publish(proof);
|
|
491
|
+
|
|
492
|
+
const fetched = await prover.fetchProof(proof.proofId);
|
|
493
|
+
|
|
494
|
+
expect(fetched).toBeDefined();
|
|
495
|
+
expect(fetched?.proofId).toBe(proof.proofId);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it("should return undefined for unknown proof", async () => {
|
|
499
|
+
const fetched = await prover.fetchProof("unknown-proof-id");
|
|
500
|
+
|
|
501
|
+
expect(fetched).toBeUndefined();
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
describe("isPublished", () => {
|
|
506
|
+
it("should return true for published proof", async () => {
|
|
507
|
+
const events = createMockEvents(3);
|
|
508
|
+
const genesisHash = await prover.getGenesisHash();
|
|
509
|
+
const witness = await buildHashChainWitness(events, genesisHash);
|
|
510
|
+
|
|
511
|
+
const proof = await prover.proveHashChain({
|
|
512
|
+
events,
|
|
513
|
+
genesisHash,
|
|
514
|
+
expectedRootHash: witness.computedRollingHash,
|
|
515
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
await prover.publish(proof);
|
|
519
|
+
|
|
520
|
+
const published = await prover.isPublished(proof.proofId);
|
|
521
|
+
|
|
522
|
+
expect(published).toBe(true);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it("should return false for unknown proof", async () => {
|
|
526
|
+
const published = await prover.isPublished("unknown-proof-id");
|
|
527
|
+
|
|
528
|
+
expect(published).toBe(false);
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
describe("waitForConfirmation", () => {
|
|
533
|
+
it("should wait for proof confirmation", async () => {
|
|
534
|
+
const events = createMockEvents(3);
|
|
535
|
+
const genesisHash = await prover.getGenesisHash();
|
|
536
|
+
const witness = await buildHashChainWitness(events, genesisHash);
|
|
537
|
+
|
|
538
|
+
const proof = await prover.proveHashChain({
|
|
539
|
+
events,
|
|
540
|
+
genesisHash,
|
|
541
|
+
expectedRootHash: witness.computedRollingHash,
|
|
542
|
+
cardanoAnchorTxHash: createMockHash("anchor"),
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
await prover.publish(proof);
|
|
546
|
+
|
|
547
|
+
const confirmed = await prover.waitForConfirmation(proof.proofId, 15000);
|
|
548
|
+
|
|
549
|
+
expect(confirmed).toBe(true);
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// =============================================================================
|
|
555
|
+
// FACTORY FUNCTION TESTS
|
|
556
|
+
// =============================================================================
|
|
557
|
+
|
|
558
|
+
describe("createMidnightProver", () => {
|
|
559
|
+
it("should create a prover instance", () => {
|
|
560
|
+
const prover = createMidnightProver();
|
|
561
|
+
expect(prover).toBeInstanceOf(DefaultMidnightProver);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("should create a prover with options", () => {
|
|
565
|
+
const prover = createMidnightProver({
|
|
566
|
+
debug: true,
|
|
567
|
+
enableInference: false,
|
|
568
|
+
});
|
|
569
|
+
expect(prover).toBeInstanceOf(DefaultMidnightProver);
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// =============================================================================
|
|
574
|
+
// PROVER REGISTRY TESTS
|
|
575
|
+
// =============================================================================
|
|
576
|
+
|
|
577
|
+
describe("proverRegistry", () => {
|
|
578
|
+
it("should have default prover registered", () => {
|
|
579
|
+
const proverNames = proverRegistry.listProvers();
|
|
580
|
+
expect(proverNames).toContain("default");
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it("should return default prover", () => {
|
|
584
|
+
const prover = proverRegistry.getDefault();
|
|
585
|
+
expect(prover).toBeDefined();
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it("should get prover by name", () => {
|
|
589
|
+
const prover = proverRegistry.get("default");
|
|
590
|
+
expect(prover).toBeDefined();
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// =============================================================================
|
|
595
|
+
// INTEGRATION: FULL FLOW TESTS
|
|
596
|
+
// =============================================================================
|
|
597
|
+
|
|
598
|
+
describe("Full Integration Flow", () => {
|
|
599
|
+
let prover: DefaultMidnightProver;
|
|
600
|
+
let linker: CardanoAnchorLinker;
|
|
601
|
+
const config = createMockConfig();
|
|
602
|
+
|
|
603
|
+
beforeEach(async () => {
|
|
604
|
+
prover = new DefaultMidnightProver({ debug: false });
|
|
605
|
+
linker = new CardanoAnchorLinker({ debug: false });
|
|
606
|
+
await prover.connect(config);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
afterEach(async () => {
|
|
610
|
+
await prover.disconnect();
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it("should complete full proof lifecycle: generate -> publish -> link -> verify", async () => {
|
|
614
|
+
// 1. Generate proof
|
|
615
|
+
const events = createMockEvents(5);
|
|
616
|
+
const genesisHash = await prover.getGenesisHash();
|
|
617
|
+
const witness = await buildHashChainWitness(events, genesisHash);
|
|
618
|
+
const cardanoAnchorTxHash = createMockHash("cardano-anchor");
|
|
619
|
+
|
|
620
|
+
const proof = await prover.proveHashChain({
|
|
621
|
+
events,
|
|
622
|
+
genesisHash,
|
|
623
|
+
expectedRootHash: witness.computedRollingHash,
|
|
624
|
+
cardanoAnchorTxHash,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
expect(proof).toBeDefined();
|
|
628
|
+
expect(proof.proofType).toBe("hash-chain");
|
|
629
|
+
|
|
630
|
+
// 2. Publish proof
|
|
631
|
+
const publicationResult = await prover.publish(proof);
|
|
632
|
+
|
|
633
|
+
expect(publicationResult.midnightTxHash).toBeDefined();
|
|
634
|
+
expect(publicationResult.proofId).toBe(proof.proofId);
|
|
635
|
+
|
|
636
|
+
// 3. Create cross-chain link
|
|
637
|
+
const link = linker.linkToAnchor(
|
|
638
|
+
proof,
|
|
639
|
+
cardanoAnchorTxHash,
|
|
640
|
+
publicationResult.midnightTxHash
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
expect(link).toBeDefined();
|
|
644
|
+
expect(link.midnightProofId).toBe(proof.proofId);
|
|
645
|
+
expect(link.midnightTxHash).toBe(publicationResult.midnightTxHash);
|
|
646
|
+
expect(link.cardanoAnchorTxHash).toBe(cardanoAnchorTxHash.toLowerCase());
|
|
647
|
+
|
|
648
|
+
// 4. Verify link
|
|
649
|
+
const linkVerification = await linker.verifyLink(link);
|
|
650
|
+
|
|
651
|
+
expect(linkVerification.valid).toBe(true);
|
|
652
|
+
expect(linkVerification.commitmentVerified).toBe(true);
|
|
653
|
+
|
|
654
|
+
// 5. Verify proof
|
|
655
|
+
const proofVerification = await prover.verify(proof);
|
|
656
|
+
|
|
657
|
+
expect(proofVerification.valid).toBe(true);
|
|
658
|
+
|
|
659
|
+
// 6. Check publication status
|
|
660
|
+
const published = await prover.isPublished(proof.proofId);
|
|
661
|
+
|
|
662
|
+
expect(published).toBe(true);
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it("should handle multiple proof types for the same anchor", async () => {
|
|
666
|
+
const cardanoAnchorTxHash = createMockHash("shared-anchor");
|
|
667
|
+
|
|
668
|
+
// Generate hash-chain proof
|
|
669
|
+
const events = createMockEvents(3);
|
|
670
|
+
const genesisHash = await prover.getGenesisHash();
|
|
671
|
+
const witness = await buildHashChainWitness(events, genesisHash);
|
|
672
|
+
|
|
673
|
+
const hashChainProof = await prover.proveHashChain({
|
|
674
|
+
events,
|
|
675
|
+
genesisHash,
|
|
676
|
+
expectedRootHash: witness.computedRollingHash,
|
|
677
|
+
cardanoAnchorTxHash,
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
// Generate policy proof
|
|
681
|
+
const policyProof = await prover.provePolicyCompliance({
|
|
682
|
+
promptHash: createMockHash("prompt"),
|
|
683
|
+
outputHash: createMockHash("output"),
|
|
684
|
+
policy: {
|
|
685
|
+
id: "test-policy",
|
|
686
|
+
version: "1.0.0",
|
|
687
|
+
rules: [],
|
|
688
|
+
},
|
|
689
|
+
cardanoAnchorTxHash,
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// Publish both
|
|
693
|
+
const hashChainResult = await prover.publish(hashChainProof);
|
|
694
|
+
const policyResult = await prover.publish(policyProof);
|
|
695
|
+
|
|
696
|
+
// Create links
|
|
697
|
+
const hashChainLink = linker.linkToAnchor(
|
|
698
|
+
hashChainProof,
|
|
699
|
+
cardanoAnchorTxHash,
|
|
700
|
+
hashChainResult.midnightTxHash
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
const policyLink = linker.linkToAnchor(
|
|
704
|
+
policyProof,
|
|
705
|
+
cardanoAnchorTxHash,
|
|
706
|
+
policyResult.midnightTxHash
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
// Both links should reference the same Cardano anchor
|
|
710
|
+
expect(hashChainLink.cardanoAnchorTxHash).toBe(policyLink.cardanoAnchorTxHash);
|
|
711
|
+
|
|
712
|
+
// Should be able to find both links by anchor
|
|
713
|
+
const anchorLinks = linker.getLinksByCardanoAnchor(cardanoAnchorTxHash);
|
|
714
|
+
expect(anchorLinks).toHaveLength(2);
|
|
715
|
+
});
|
|
716
|
+
});
|