@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,644 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Unit tests for proof publication and cross-chain linking.
|
|
3
|
+
*
|
|
4
|
+
* Location: packages/midnight-prover/src/__tests__/proof-publication.test.ts
|
|
5
|
+
*
|
|
6
|
+
* Summary:
|
|
7
|
+
* Tests for ProofPublisher, CardanoAnchorLinker, and ProofServerClient classes.
|
|
8
|
+
* Covers proof publication, status monitoring, cross-chain link creation,
|
|
9
|
+
* and link verification.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* Run with: pnpm test -- packages/midnight-prover
|
|
13
|
+
*
|
|
14
|
+
* Related files:
|
|
15
|
+
* - linking/proof-publication.ts: ProofPublisher implementation
|
|
16
|
+
* - linking/cardano-anchor-link.ts: CardanoAnchorLinker implementation
|
|
17
|
+
* - midnight/proof-server-client.ts: ProofServerClient implementation
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
ProofPublisher,
|
|
24
|
+
createProofPublisher,
|
|
25
|
+
type ProofPublisherOptions,
|
|
26
|
+
} from "../linking/proof-publication.js";
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
CardanoAnchorLinker,
|
|
30
|
+
createCardanoAnchorLinker,
|
|
31
|
+
type CrossChainLink,
|
|
32
|
+
} from "../linking/cardano-anchor-link.js";
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
ProofServerClient,
|
|
36
|
+
createProofServerClient,
|
|
37
|
+
} from "../midnight/proof-server-client.js";
|
|
38
|
+
|
|
39
|
+
import {
|
|
40
|
+
MidnightProverError,
|
|
41
|
+
MidnightProverException,
|
|
42
|
+
type HashChainProof,
|
|
43
|
+
type PolicyProof,
|
|
44
|
+
type ProofServerConfig,
|
|
45
|
+
} from "../types.js";
|
|
46
|
+
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// TEST HELPERS
|
|
49
|
+
// =============================================================================
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create a valid 64-character hex hash.
|
|
53
|
+
*/
|
|
54
|
+
function createMockHash(seed: string): string {
|
|
55
|
+
const hex = "0123456789abcdef";
|
|
56
|
+
let result = "";
|
|
57
|
+
for (let i = 0; i < 64; i++) {
|
|
58
|
+
result += hex[(seed.charCodeAt(i % seed.length) + i) % 16];
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a mock hash-chain proof for testing.
|
|
65
|
+
*/
|
|
66
|
+
function createMockHashChainProof(cardanoAnchorTxHash?: string): HashChainProof {
|
|
67
|
+
return {
|
|
68
|
+
proofType: "hash-chain",
|
|
69
|
+
proofId: `proof-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
70
|
+
proof: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
|
|
71
|
+
createdAt: new Date().toISOString(),
|
|
72
|
+
provingTimeMs: 100,
|
|
73
|
+
proofSizeBytes: 8,
|
|
74
|
+
publicInputs: {
|
|
75
|
+
rootHash: createMockHash("root"),
|
|
76
|
+
eventCount: 10,
|
|
77
|
+
cardanoAnchorTxHash: cardanoAnchorTxHash ?? createMockHash("anchor"),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create a mock policy proof for testing.
|
|
84
|
+
*/
|
|
85
|
+
function createMockPolicyProof(cardanoAnchorTxHash?: string): PolicyProof {
|
|
86
|
+
return {
|
|
87
|
+
proofType: "policy-compliance",
|
|
88
|
+
proofId: `policy-proof-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
89
|
+
proof: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
|
|
90
|
+
createdAt: new Date().toISOString(),
|
|
91
|
+
provingTimeMs: 50,
|
|
92
|
+
proofSizeBytes: 8,
|
|
93
|
+
publicInputs: {
|
|
94
|
+
promptHash: createMockHash("prompt"),
|
|
95
|
+
policyId: "test-policy",
|
|
96
|
+
policyVersion: "1.0.0",
|
|
97
|
+
compliant: true,
|
|
98
|
+
cardanoAnchorTxHash: cardanoAnchorTxHash ?? createMockHash("anchor"),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create a mock proof server config.
|
|
105
|
+
*/
|
|
106
|
+
function createMockConfig(): ProofServerConfig {
|
|
107
|
+
return {
|
|
108
|
+
proofServerUrl: "https://mock.proof.server",
|
|
109
|
+
apiKey: "test-api-key",
|
|
110
|
+
timeout: 30000,
|
|
111
|
+
retries: 3,
|
|
112
|
+
circuitCacheDir: undefined,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// =============================================================================
|
|
117
|
+
// PROOF PUBLISHER TESTS
|
|
118
|
+
// =============================================================================
|
|
119
|
+
|
|
120
|
+
describe("ProofPublisher", () => {
|
|
121
|
+
let publisher: ProofPublisher;
|
|
122
|
+
const config = createMockConfig();
|
|
123
|
+
|
|
124
|
+
beforeEach(async () => {
|
|
125
|
+
publisher = new ProofPublisher({
|
|
126
|
+
debug: false,
|
|
127
|
+
maxRetries: 2,
|
|
128
|
+
retryDelayMs: 10,
|
|
129
|
+
pollIntervalMs: 50,
|
|
130
|
+
});
|
|
131
|
+
await publisher.connect(config);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
afterEach(async () => {
|
|
135
|
+
await publisher.disconnect();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("constructor", () => {
|
|
139
|
+
it("should create a publisher with default options", () => {
|
|
140
|
+
const defaultPublisher = new ProofPublisher();
|
|
141
|
+
expect(defaultPublisher).toBeInstanceOf(ProofPublisher);
|
|
142
|
+
expect(defaultPublisher.isConnected()).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should create a publisher with custom options", () => {
|
|
146
|
+
const customPublisher = new ProofPublisher({
|
|
147
|
+
maxRetries: 5,
|
|
148
|
+
retryDelayMs: 500,
|
|
149
|
+
debug: true,
|
|
150
|
+
});
|
|
151
|
+
expect(customPublisher).toBeInstanceOf(ProofPublisher);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("connect/disconnect", () => {
|
|
156
|
+
it("should connect successfully", async () => {
|
|
157
|
+
const newPublisher = new ProofPublisher();
|
|
158
|
+
expect(newPublisher.isConnected()).toBe(false);
|
|
159
|
+
|
|
160
|
+
await newPublisher.connect(config);
|
|
161
|
+
expect(newPublisher.isConnected()).toBe(true);
|
|
162
|
+
|
|
163
|
+
await newPublisher.disconnect();
|
|
164
|
+
expect(newPublisher.isConnected()).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("should throw for invalid config", async () => {
|
|
168
|
+
const newPublisher = new ProofPublisher();
|
|
169
|
+
|
|
170
|
+
await expect(
|
|
171
|
+
newPublisher.connect({
|
|
172
|
+
proofServerUrl: "",
|
|
173
|
+
apiKey: undefined,
|
|
174
|
+
timeout: 30000,
|
|
175
|
+
retries: 3,
|
|
176
|
+
circuitCacheDir: undefined,
|
|
177
|
+
})
|
|
178
|
+
).rejects.toThrow(MidnightProverException);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe("publish", () => {
|
|
183
|
+
it("should publish a proof successfully", async () => {
|
|
184
|
+
const proof = createMockHashChainProof();
|
|
185
|
+
|
|
186
|
+
const result = await publisher.publish(proof);
|
|
187
|
+
|
|
188
|
+
expect(result).toBeDefined();
|
|
189
|
+
expect(result.midnightTxHash).toMatch(/^[0-9a-f]{64}$/);
|
|
190
|
+
expect(result.proofId).toBe(proof.proofId);
|
|
191
|
+
expect(result.cardanoAnchorTxHash).toBe(proof.publicInputs.cardanoAnchorTxHash);
|
|
192
|
+
expect(result.timestamp).toBeDefined();
|
|
193
|
+
expect(result.blockNumber).toBeGreaterThan(0);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("should publish different proof types", async () => {
|
|
197
|
+
const hashChainProof = createMockHashChainProof();
|
|
198
|
+
const policyProof = createMockPolicyProof();
|
|
199
|
+
|
|
200
|
+
const result1 = await publisher.publish(hashChainProof);
|
|
201
|
+
const result2 = await publisher.publish(policyProof);
|
|
202
|
+
|
|
203
|
+
expect(result1.proofId).toBe(hashChainProof.proofId);
|
|
204
|
+
expect(result2.proofId).toBe(policyProof.proofId);
|
|
205
|
+
expect(result1.midnightTxHash).not.toBe(result2.midnightTxHash);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("should throw for proof without cardanoAnchorTxHash", async () => {
|
|
209
|
+
const invalidProof = {
|
|
210
|
+
proofType: "hash-chain",
|
|
211
|
+
proofId: "test-proof",
|
|
212
|
+
proof: new Uint8Array([1, 2, 3]),
|
|
213
|
+
createdAt: new Date().toISOString(),
|
|
214
|
+
provingTimeMs: 100,
|
|
215
|
+
proofSizeBytes: 3,
|
|
216
|
+
publicInputs: {
|
|
217
|
+
rootHash: createMockHash("root"),
|
|
218
|
+
eventCount: 10,
|
|
219
|
+
// Missing cardanoAnchorTxHash
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
await expect(publisher.publish(invalidProof as HashChainProof)).rejects.toThrow(
|
|
224
|
+
MidnightProverException
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("should throw when not connected", async () => {
|
|
229
|
+
await publisher.disconnect();
|
|
230
|
+
const proof = createMockHashChainProof();
|
|
231
|
+
|
|
232
|
+
await expect(publisher.publish(proof)).rejects.toThrow(MidnightProverException);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe("getProofStatus", () => {
|
|
237
|
+
it("should return pending status for newly published proof", async () => {
|
|
238
|
+
const proof = createMockHashChainProof();
|
|
239
|
+
await publisher.publish(proof);
|
|
240
|
+
|
|
241
|
+
const status = await publisher.getProofStatus(proof.proofId);
|
|
242
|
+
|
|
243
|
+
expect(status.status).toBe("pending");
|
|
244
|
+
expect(status.proofId).toBe(proof.proofId);
|
|
245
|
+
expect(status.midnightTxHash).toBeDefined();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("should return not_found for unknown proof", async () => {
|
|
249
|
+
const status = await publisher.getProofStatus("unknown-proof-id");
|
|
250
|
+
|
|
251
|
+
expect(status.status).toBe("not_found");
|
|
252
|
+
expect(status.midnightTxHash).toBeUndefined();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe("waitForConfirmation", () => {
|
|
257
|
+
it("should wait for confirmation", async () => {
|
|
258
|
+
const proof = createMockHashChainProof();
|
|
259
|
+
await publisher.publish(proof);
|
|
260
|
+
|
|
261
|
+
// With mock implementation, confirmation happens after ~10 seconds simulated time
|
|
262
|
+
// but with fast polling, it should confirm quickly
|
|
263
|
+
const confirmed = await publisher.waitForConfirmation(proof.proofId, 15000);
|
|
264
|
+
|
|
265
|
+
expect(confirmed).toBe(true);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should return false for unknown proof", async () => {
|
|
269
|
+
const confirmed = await publisher.waitForConfirmation("unknown-proof", 100);
|
|
270
|
+
|
|
271
|
+
expect(confirmed).toBe(false);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe("getPublicationResult", () => {
|
|
276
|
+
it("should return publication result for published proof", async () => {
|
|
277
|
+
const proof = createMockHashChainProof();
|
|
278
|
+
const publishResult = await publisher.publish(proof);
|
|
279
|
+
|
|
280
|
+
const cachedResult = publisher.getPublicationResult(proof.proofId);
|
|
281
|
+
|
|
282
|
+
expect(cachedResult).toBeDefined();
|
|
283
|
+
expect(cachedResult?.midnightTxHash).toBe(publishResult.midnightTxHash);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("should return undefined for unknown proof", () => {
|
|
287
|
+
const result = publisher.getPublicationResult("unknown-proof");
|
|
288
|
+
|
|
289
|
+
expect(result).toBeUndefined();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe("createProofPublisher", () => {
|
|
295
|
+
it("should create a publisher instance", () => {
|
|
296
|
+
const publisher = createProofPublisher();
|
|
297
|
+
expect(publisher).toBeInstanceOf(ProofPublisher);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should create a publisher with options", () => {
|
|
301
|
+
const publisher = createProofPublisher({
|
|
302
|
+
maxRetries: 5,
|
|
303
|
+
debug: true,
|
|
304
|
+
});
|
|
305
|
+
expect(publisher).toBeInstanceOf(ProofPublisher);
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// =============================================================================
|
|
310
|
+
// CARDANO ANCHOR LINKER TESTS
|
|
311
|
+
// =============================================================================
|
|
312
|
+
|
|
313
|
+
describe("CardanoAnchorLinker", () => {
|
|
314
|
+
let linker: CardanoAnchorLinker;
|
|
315
|
+
|
|
316
|
+
beforeEach(() => {
|
|
317
|
+
linker = new CardanoAnchorLinker({ debug: false });
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe("constructor", () => {
|
|
321
|
+
it("should create a linker with default options", () => {
|
|
322
|
+
const defaultLinker = new CardanoAnchorLinker();
|
|
323
|
+
expect(defaultLinker).toBeInstanceOf(CardanoAnchorLinker);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("should create a linker with custom verifiers", () => {
|
|
327
|
+
const customLinker = new CardanoAnchorLinker({
|
|
328
|
+
cardanoVerifier: async () => true,
|
|
329
|
+
midnightVerifier: async () => true,
|
|
330
|
+
});
|
|
331
|
+
expect(customLinker).toBeInstanceOf(CardanoAnchorLinker);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe("linkToAnchor", () => {
|
|
336
|
+
it("should create a cross-chain link for hash-chain proof", () => {
|
|
337
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
338
|
+
const proof = createMockHashChainProof(cardanoTxHash);
|
|
339
|
+
|
|
340
|
+
const link = linker.linkToAnchor(proof, cardanoTxHash);
|
|
341
|
+
|
|
342
|
+
expect(link).toBeDefined();
|
|
343
|
+
expect(link.linkId).toMatch(/^link-[0-9a-f]+$/);
|
|
344
|
+
expect(link.version).toBe("1.0.0");
|
|
345
|
+
expect(link.midnightProofId).toBe(proof.proofId);
|
|
346
|
+
expect(link.proofType).toBe("hash-chain");
|
|
347
|
+
expect(link.cardanoAnchorTxHash).toBe(cardanoTxHash.toLowerCase());
|
|
348
|
+
expect(link.anchoredRootHash).toBe(proof.publicInputs.rootHash);
|
|
349
|
+
expect(link.linkCommitment).toMatch(/^[0-9a-f]{64}$/);
|
|
350
|
+
expect(link.createdAt).toBeDefined();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("should create a cross-chain link for policy proof", () => {
|
|
354
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
355
|
+
const proof = createMockPolicyProof(cardanoTxHash);
|
|
356
|
+
|
|
357
|
+
const link = linker.linkToAnchor(proof, cardanoTxHash);
|
|
358
|
+
|
|
359
|
+
expect(link.proofType).toBe("policy-compliance");
|
|
360
|
+
expect(link.anchoredRootHash).toBe(proof.publicInputs.promptHash);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("should include midnightTxHash when provided", () => {
|
|
364
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
365
|
+
const midnightTxHash = createMockHash("midnight-tx");
|
|
366
|
+
const proof = createMockHashChainProof(cardanoTxHash);
|
|
367
|
+
|
|
368
|
+
const link = linker.linkToAnchor(proof, cardanoTxHash, midnightTxHash);
|
|
369
|
+
|
|
370
|
+
expect(link.midnightTxHash).toBe(midnightTxHash);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("should throw for mismatched cardanoAnchorTxHash", () => {
|
|
374
|
+
const proofCardanoTx = createMockHash("proof-cardano");
|
|
375
|
+
const differentCardanoTx = createMockHash("different-cardano");
|
|
376
|
+
const proof = createMockHashChainProof(proofCardanoTx);
|
|
377
|
+
|
|
378
|
+
expect(() => linker.linkToAnchor(proof, differentCardanoTx)).toThrow(
|
|
379
|
+
MidnightProverException
|
|
380
|
+
);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it("should throw for invalid cardano tx hash format", () => {
|
|
384
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
385
|
+
const proof = createMockHashChainProof(cardanoTxHash);
|
|
386
|
+
|
|
387
|
+
expect(() => linker.linkToAnchor(proof, "invalid-hash")).toThrow(
|
|
388
|
+
MidnightProverException
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe("verifyLink", () => {
|
|
394
|
+
it("should verify a valid link", async () => {
|
|
395
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
396
|
+
const proof = createMockHashChainProof(cardanoTxHash);
|
|
397
|
+
const link = linker.linkToAnchor(proof, cardanoTxHash);
|
|
398
|
+
|
|
399
|
+
const result = await linker.verifyLink(link);
|
|
400
|
+
|
|
401
|
+
expect(result.valid).toBe(true);
|
|
402
|
+
expect(result.linkId).toBe(link.linkId);
|
|
403
|
+
expect(result.commitmentVerified).toBe(true);
|
|
404
|
+
expect(result.errors).toHaveLength(0);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it("should detect tampered commitment", async () => {
|
|
408
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
409
|
+
const proof = createMockHashChainProof(cardanoTxHash);
|
|
410
|
+
const link = linker.linkToAnchor(proof, cardanoTxHash);
|
|
411
|
+
|
|
412
|
+
// Tamper with commitment
|
|
413
|
+
const tamperedLink: CrossChainLink = {
|
|
414
|
+
...link,
|
|
415
|
+
linkCommitment: createMockHash("tampered"),
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const result = await linker.verifyLink(tamperedLink);
|
|
419
|
+
|
|
420
|
+
expect(result.valid).toBe(false);
|
|
421
|
+
expect(result.errors).toContain("Link commitment does not match");
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("should detect invalid link structure", async () => {
|
|
425
|
+
const invalidLink = {
|
|
426
|
+
linkId: "test-link",
|
|
427
|
+
version: "1.0.0",
|
|
428
|
+
midnightProofId: "",
|
|
429
|
+
proofType: "hash-chain",
|
|
430
|
+
publicInputsHash: "invalid",
|
|
431
|
+
cardanoAnchorTxHash: "invalid",
|
|
432
|
+
anchoredRootHash: "invalid",
|
|
433
|
+
createdAt: new Date().toISOString(),
|
|
434
|
+
linkCommitment: "invalid",
|
|
435
|
+
} as CrossChainLink;
|
|
436
|
+
|
|
437
|
+
const result = await linker.verifyLink(invalidLink);
|
|
438
|
+
|
|
439
|
+
expect(result.valid).toBe(false);
|
|
440
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
describe("cache operations", () => {
|
|
445
|
+
it("should cache created links", () => {
|
|
446
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
447
|
+
const proof = createMockHashChainProof(cardanoTxHash);
|
|
448
|
+
const link = linker.linkToAnchor(proof, cardanoTxHash);
|
|
449
|
+
|
|
450
|
+
const cached = linker.getCachedLink(link.linkId);
|
|
451
|
+
|
|
452
|
+
expect(cached).toBeDefined();
|
|
453
|
+
expect(cached?.linkId).toBe(link.linkId);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it("should find link by proof ID", () => {
|
|
457
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
458
|
+
const proof = createMockHashChainProof(cardanoTxHash);
|
|
459
|
+
linker.linkToAnchor(proof, cardanoTxHash);
|
|
460
|
+
|
|
461
|
+
const found = linker.getLinkByProofId(proof.proofId);
|
|
462
|
+
|
|
463
|
+
expect(found).toBeDefined();
|
|
464
|
+
expect(found?.midnightProofId).toBe(proof.proofId);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it("should find links by cardano anchor", () => {
|
|
468
|
+
const cardanoTxHash = createMockHash("cardano-tx");
|
|
469
|
+
const proof1 = createMockHashChainProof(cardanoTxHash);
|
|
470
|
+
const proof2 = createMockPolicyProof(cardanoTxHash);
|
|
471
|
+
|
|
472
|
+
linker.linkToAnchor(proof1, cardanoTxHash);
|
|
473
|
+
linker.linkToAnchor(proof2, cardanoTxHash);
|
|
474
|
+
|
|
475
|
+
const links = linker.getLinksByCardanoAnchor(cardanoTxHash);
|
|
476
|
+
|
|
477
|
+
expect(links).toHaveLength(2);
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
describe("createCardanoAnchorLinker", () => {
|
|
483
|
+
it("should create a linker instance", () => {
|
|
484
|
+
const linker = createCardanoAnchorLinker();
|
|
485
|
+
expect(linker).toBeInstanceOf(CardanoAnchorLinker);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it("should create a linker with options", () => {
|
|
489
|
+
const linker = createCardanoAnchorLinker({
|
|
490
|
+
debug: true,
|
|
491
|
+
});
|
|
492
|
+
expect(linker).toBeInstanceOf(CardanoAnchorLinker);
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// =============================================================================
|
|
497
|
+
// PROOF SERVER CLIENT TESTS
|
|
498
|
+
// =============================================================================
|
|
499
|
+
|
|
500
|
+
describe("ProofServerClient", () => {
|
|
501
|
+
let client: ProofServerClient;
|
|
502
|
+
const config = createMockConfig();
|
|
503
|
+
|
|
504
|
+
beforeEach(async () => {
|
|
505
|
+
client = new ProofServerClient({
|
|
506
|
+
debug: false,
|
|
507
|
+
simulatedProvingTimeMs: 10,
|
|
508
|
+
});
|
|
509
|
+
await client.connect(config);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
afterEach(async () => {
|
|
513
|
+
await client.disconnect();
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
describe("constructor", () => {
|
|
517
|
+
it("should create a client with default options", () => {
|
|
518
|
+
const defaultClient = new ProofServerClient();
|
|
519
|
+
expect(defaultClient).toBeInstanceOf(ProofServerClient);
|
|
520
|
+
expect(defaultClient.isConnected()).toBe(false);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
describe("connect/disconnect", () => {
|
|
525
|
+
it("should connect and disconnect successfully", async () => {
|
|
526
|
+
const newClient = new ProofServerClient();
|
|
527
|
+
|
|
528
|
+
expect(newClient.isConnected()).toBe(false);
|
|
529
|
+
await newClient.connect(config);
|
|
530
|
+
expect(newClient.isConnected()).toBe(true);
|
|
531
|
+
expect(newClient.getConfig()).toBeDefined();
|
|
532
|
+
|
|
533
|
+
await newClient.disconnect();
|
|
534
|
+
expect(newClient.isConnected()).toBe(false);
|
|
535
|
+
expect(newClient.getConfig()).toBeUndefined();
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it("should throw for invalid config", async () => {
|
|
539
|
+
const newClient = new ProofServerClient();
|
|
540
|
+
|
|
541
|
+
await expect(
|
|
542
|
+
newClient.connect({
|
|
543
|
+
proofServerUrl: "",
|
|
544
|
+
apiKey: undefined,
|
|
545
|
+
timeout: 30000,
|
|
546
|
+
retries: 3,
|
|
547
|
+
circuitCacheDir: undefined,
|
|
548
|
+
})
|
|
549
|
+
).rejects.toThrow(MidnightProverException);
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
describe("submitProof", () => {
|
|
554
|
+
it("should submit a proof successfully", async () => {
|
|
555
|
+
const witness = { events: [], genesisHash: createMockHash("genesis") };
|
|
556
|
+
const publicInputs = { rootHash: createMockHash("root"), eventCount: 0 };
|
|
557
|
+
|
|
558
|
+
const result = await client.submitProof("hash-chain", witness, publicInputs);
|
|
559
|
+
|
|
560
|
+
expect(result).toBeDefined();
|
|
561
|
+
expect(result.proofId).toMatch(/^proof-[0-9a-f]+$/);
|
|
562
|
+
expect(result.proof).toBeInstanceOf(Uint8Array);
|
|
563
|
+
expect(result.proof.length).toBeGreaterThan(0);
|
|
564
|
+
expect(result.provingTimeMs).toBeGreaterThanOrEqual(0);
|
|
565
|
+
expect(result.circuit).toBe("poi_hash_chain_v1");
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it("should submit different circuit types", async () => {
|
|
569
|
+
const witness = {};
|
|
570
|
+
const publicInputs = {};
|
|
571
|
+
|
|
572
|
+
const hashChainResult = await client.submitProof("hash-chain", witness, publicInputs);
|
|
573
|
+
const policyResult = await client.submitProof("policy-compliance", witness, publicInputs);
|
|
574
|
+
|
|
575
|
+
expect(hashChainResult.circuit).toBe("poi_hash_chain_v1");
|
|
576
|
+
expect(policyResult.circuit).toBe("poi_policy_compliance_v1");
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("should throw when not connected", async () => {
|
|
580
|
+
await client.disconnect();
|
|
581
|
+
|
|
582
|
+
await expect(client.submitProof("hash-chain", {}, {})).rejects.toThrow(
|
|
583
|
+
MidnightProverException
|
|
584
|
+
);
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
describe("getCircuitInfo", () => {
|
|
589
|
+
it("should return circuit information", async () => {
|
|
590
|
+
const info = await client.getCircuitInfo("poi_hash_chain_v1");
|
|
591
|
+
|
|
592
|
+
expect(info).toBeDefined();
|
|
593
|
+
expect(info.name).toBe("poi_hash_chain_v1");
|
|
594
|
+
expect(info.version).toBeDefined();
|
|
595
|
+
expect(info.constraintCount).toBeGreaterThan(0);
|
|
596
|
+
expect(info.available).toBe(true);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it("should report zkml circuit as unavailable", async () => {
|
|
600
|
+
const info = await client.getCircuitInfo("poi_inference_v1");
|
|
601
|
+
|
|
602
|
+
expect(info.available).toBe(false);
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
describe("listCircuits", () => {
|
|
607
|
+
it("should return list of circuits", async () => {
|
|
608
|
+
const circuits = await client.listCircuits();
|
|
609
|
+
|
|
610
|
+
expect(circuits).toBeInstanceOf(Array);
|
|
611
|
+
expect(circuits).toContain("poi_hash_chain_v1");
|
|
612
|
+
expect(circuits).toContain("poi_policy_compliance_v1");
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
describe("isCircuitAvailable", () => {
|
|
617
|
+
it("should return true for available circuits", async () => {
|
|
618
|
+
const available = await client.isCircuitAvailable("hash-chain");
|
|
619
|
+
|
|
620
|
+
expect(available).toBe(true);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it("should return false for unavailable circuits", async () => {
|
|
624
|
+
const available = await client.isCircuitAvailable("zkml-inference");
|
|
625
|
+
|
|
626
|
+
expect(available).toBe(false);
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
describe("createProofServerClient", () => {
|
|
632
|
+
it("should create a client instance", () => {
|
|
633
|
+
const client = createProofServerClient();
|
|
634
|
+
expect(client).toBeInstanceOf(ProofServerClient);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it("should create a client with options", () => {
|
|
638
|
+
const client = createProofServerClient({
|
|
639
|
+
debug: true,
|
|
640
|
+
simulatedProvingTimeMs: 100,
|
|
641
|
+
});
|
|
642
|
+
expect(client).toBeInstanceOf(ProofServerClient);
|
|
643
|
+
});
|
|
644
|
+
});
|