@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,921 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for SelectiveDisclosureProver and Merkle utilities.
|
|
3
|
+
*
|
|
4
|
+
* Location: packages/midnight-prover/src/__tests__/selective-disclosure.test.ts
|
|
5
|
+
*
|
|
6
|
+
* Summary:
|
|
7
|
+
* This file contains unit tests for the SelectiveDisclosureProver class and
|
|
8
|
+
* associated Merkle tree utilities. Tests cover proof generation, verification,
|
|
9
|
+
* Merkle tree construction, and inclusion proof validation.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
13
|
+
import {
|
|
14
|
+
SelectiveDisclosureProver,
|
|
15
|
+
createSelectiveDisclosureProver,
|
|
16
|
+
computeSpanHash,
|
|
17
|
+
computeLeafHash,
|
|
18
|
+
computeNodeHash,
|
|
19
|
+
computeMerkleRoot,
|
|
20
|
+
generateMerkleInclusionProof,
|
|
21
|
+
verifyMerkleProof,
|
|
22
|
+
verifySpanInclusion,
|
|
23
|
+
} from "../proofs/selective-disclosure.js";
|
|
24
|
+
import type {
|
|
25
|
+
DisclosureInput,
|
|
26
|
+
} from "../types.js";
|
|
27
|
+
import type {
|
|
28
|
+
TraceBundle,
|
|
29
|
+
TraceRun,
|
|
30
|
+
TraceSpan,
|
|
31
|
+
TraceEvent,
|
|
32
|
+
TraceBundlePublicView,
|
|
33
|
+
CommandEvent,
|
|
34
|
+
OutputEvent,
|
|
35
|
+
MerkleProof,
|
|
36
|
+
} from "@fluxpointstudios/orynq-sdk-process-trace";
|
|
37
|
+
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// TEST FIXTURES
|
|
40
|
+
// =============================================================================
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate a mock 64-character hex hash.
|
|
44
|
+
*/
|
|
45
|
+
function mockHash(seed: string): string {
|
|
46
|
+
let hash = "";
|
|
47
|
+
for (let i = 0; i < 64; i++) {
|
|
48
|
+
const charCode = seed.charCodeAt(i % seed.length) || 0;
|
|
49
|
+
hash += ((charCode + i) % 16).toString(16);
|
|
50
|
+
}
|
|
51
|
+
return hash;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a command event.
|
|
56
|
+
*/
|
|
57
|
+
function createCommandEvent(overrides: Partial<CommandEvent> = {}): CommandEvent {
|
|
58
|
+
const id = overrides.id ?? crypto.randomUUID();
|
|
59
|
+
return {
|
|
60
|
+
kind: "command",
|
|
61
|
+
id,
|
|
62
|
+
seq: 0,
|
|
63
|
+
timestamp: "2024-01-15T10:30:00.000Z",
|
|
64
|
+
visibility: "public",
|
|
65
|
+
command: "npm install",
|
|
66
|
+
hash: mockHash(`event-${id}`),
|
|
67
|
+
...overrides,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create an output event.
|
|
73
|
+
*/
|
|
74
|
+
function createOutputEvent(overrides: Partial<OutputEvent> = {}): OutputEvent {
|
|
75
|
+
const id = overrides.id ?? crypto.randomUUID();
|
|
76
|
+
return {
|
|
77
|
+
kind: "output",
|
|
78
|
+
id,
|
|
79
|
+
seq: 1,
|
|
80
|
+
timestamp: "2024-01-15T10:30:01.000Z",
|
|
81
|
+
visibility: "private",
|
|
82
|
+
stream: "stdout",
|
|
83
|
+
content: "done",
|
|
84
|
+
hash: mockHash(`event-${id}`),
|
|
85
|
+
...overrides,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create a trace span.
|
|
91
|
+
*/
|
|
92
|
+
function createSpan(overrides: Partial<TraceSpan> = {}): TraceSpan {
|
|
93
|
+
return {
|
|
94
|
+
id: crypto.randomUUID(),
|
|
95
|
+
spanSeq: 0,
|
|
96
|
+
name: "test-span",
|
|
97
|
+
status: "completed",
|
|
98
|
+
visibility: "public",
|
|
99
|
+
startedAt: "2024-01-15T10:30:00.000Z",
|
|
100
|
+
endedAt: "2024-01-15T10:31:00.000Z",
|
|
101
|
+
durationMs: 60000,
|
|
102
|
+
eventIds: [],
|
|
103
|
+
childSpanIds: [],
|
|
104
|
+
...overrides,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create a trace run.
|
|
110
|
+
*/
|
|
111
|
+
function createTraceRun(
|
|
112
|
+
spans: TraceSpan[],
|
|
113
|
+
events: TraceEvent[],
|
|
114
|
+
overrides: Partial<TraceRun> = {}
|
|
115
|
+
): TraceRun {
|
|
116
|
+
return {
|
|
117
|
+
id: crypto.randomUUID(),
|
|
118
|
+
schemaVersion: "1.0",
|
|
119
|
+
agentId: "test-agent",
|
|
120
|
+
status: "completed",
|
|
121
|
+
startedAt: "2024-01-15T10:30:00.000Z",
|
|
122
|
+
endedAt: "2024-01-15T10:31:00.000Z",
|
|
123
|
+
durationMs: 60000,
|
|
124
|
+
events,
|
|
125
|
+
spans,
|
|
126
|
+
rollingHash: mockHash("rolling-hash"),
|
|
127
|
+
rootHash: mockHash("root-hash"),
|
|
128
|
+
nextSeq: events.length,
|
|
129
|
+
nextSpanSeq: spans.length,
|
|
130
|
+
...overrides,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Create a public view for a bundle.
|
|
136
|
+
*/
|
|
137
|
+
function createPublicView(run: TraceRun, merkleRoot: string): TraceBundlePublicView {
|
|
138
|
+
return {
|
|
139
|
+
runId: run.id,
|
|
140
|
+
agentId: run.agentId,
|
|
141
|
+
schemaVersion: run.schemaVersion,
|
|
142
|
+
startedAt: run.startedAt,
|
|
143
|
+
endedAt: run.endedAt ?? run.startedAt,
|
|
144
|
+
durationMs: run.durationMs ?? 0,
|
|
145
|
+
status: run.status,
|
|
146
|
+
totalEvents: run.events.length,
|
|
147
|
+
totalSpans: run.spans.length,
|
|
148
|
+
rootHash: run.rootHash ?? "",
|
|
149
|
+
merkleRoot,
|
|
150
|
+
publicSpans: [],
|
|
151
|
+
redactedSpanHashes: [],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create a complete trace bundle for testing.
|
|
157
|
+
*/
|
|
158
|
+
async function createTestBundle(spanCount: number = 1): Promise<{
|
|
159
|
+
bundle: TraceBundle;
|
|
160
|
+
merkleRoot: string;
|
|
161
|
+
}> {
|
|
162
|
+
const events: TraceEvent[] = [];
|
|
163
|
+
const spans: TraceSpan[] = [];
|
|
164
|
+
|
|
165
|
+
for (let i = 0; i < spanCount; i++) {
|
|
166
|
+
const eventId = `event-${i}`;
|
|
167
|
+
const event = createCommandEvent({ id: eventId, seq: i });
|
|
168
|
+
events.push(event);
|
|
169
|
+
|
|
170
|
+
const span = createSpan({
|
|
171
|
+
id: `span-${i}`,
|
|
172
|
+
spanSeq: i,
|
|
173
|
+
name: `span-${i}`,
|
|
174
|
+
eventIds: [eventId],
|
|
175
|
+
});
|
|
176
|
+
spans.push(span);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Compute leaf hashes for Merkle root
|
|
180
|
+
const leafHashes: string[] = [];
|
|
181
|
+
for (const span of spans) {
|
|
182
|
+
const spanEvents = span.eventIds
|
|
183
|
+
.map((id) => events.find((e) => e.id === id))
|
|
184
|
+
.filter((e): e is TraceEvent => e !== undefined);
|
|
185
|
+
const eventHashes = spanEvents.map((e) => e.hash ?? "");
|
|
186
|
+
const spanHash = await computeSpanHash(span, eventHashes);
|
|
187
|
+
const leafHash = await computeLeafHash(spanHash);
|
|
188
|
+
leafHashes.push(leafHash);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const merkleRoot = await computeMerkleRoot(leafHashes);
|
|
192
|
+
const run = createTraceRun(spans, events);
|
|
193
|
+
const publicView = createPublicView(run, merkleRoot);
|
|
194
|
+
|
|
195
|
+
const bundle: TraceBundle = {
|
|
196
|
+
formatVersion: "1.0",
|
|
197
|
+
publicView,
|
|
198
|
+
privateRun: run,
|
|
199
|
+
merkleRoot,
|
|
200
|
+
rootHash: run.rootHash ?? "",
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return { bundle, merkleRoot };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// =============================================================================
|
|
207
|
+
// MERKLE UTILITY TESTS
|
|
208
|
+
// =============================================================================
|
|
209
|
+
|
|
210
|
+
describe("computeSpanHash", () => {
|
|
211
|
+
it("produces consistent output for same input", async () => {
|
|
212
|
+
const span = createSpan({ id: "fixed-span-id", spanSeq: 0, name: "build" });
|
|
213
|
+
const eventHashes = ["hash1", "hash2"];
|
|
214
|
+
|
|
215
|
+
const hash1 = await computeSpanHash(span, eventHashes);
|
|
216
|
+
const hash2 = await computeSpanHash(span, eventHashes);
|
|
217
|
+
|
|
218
|
+
expect(hash1).toBe(hash2);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("produces 64-character hex output", async () => {
|
|
222
|
+
const span = createSpan();
|
|
223
|
+
const hash = await computeSpanHash(span, []);
|
|
224
|
+
|
|
225
|
+
expect(hash).toHaveLength(64);
|
|
226
|
+
expect(hash).toMatch(/^[a-f0-9]+$/);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("produces different output for different span names", async () => {
|
|
230
|
+
const span1 = createSpan({ id: "same-id", name: "build" });
|
|
231
|
+
const span2 = createSpan({ id: "same-id", name: "test" });
|
|
232
|
+
|
|
233
|
+
const hash1 = await computeSpanHash(span1, []);
|
|
234
|
+
const hash2 = await computeSpanHash(span2, []);
|
|
235
|
+
|
|
236
|
+
expect(hash1).not.toBe(hash2);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("produces different output for different event hashes", async () => {
|
|
240
|
+
const span = createSpan({ id: "fixed-id" });
|
|
241
|
+
|
|
242
|
+
const hash1 = await computeSpanHash(span, ["event-hash-1"]);
|
|
243
|
+
const hash2 = await computeSpanHash(span, ["event-hash-2"]);
|
|
244
|
+
|
|
245
|
+
expect(hash1).not.toBe(hash2);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe("computeLeafHash", () => {
|
|
250
|
+
it("produces consistent output for same input", async () => {
|
|
251
|
+
const spanHash = mockHash("test-span");
|
|
252
|
+
|
|
253
|
+
const hash1 = await computeLeafHash(spanHash);
|
|
254
|
+
const hash2 = await computeLeafHash(spanHash);
|
|
255
|
+
|
|
256
|
+
expect(hash1).toBe(hash2);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("produces 64-character hex output", async () => {
|
|
260
|
+
const hash = await computeLeafHash(mockHash("test"));
|
|
261
|
+
expect(hash).toHaveLength(64);
|
|
262
|
+
expect(hash).toMatch(/^[a-f0-9]+$/);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("produces different output for different span hashes", async () => {
|
|
266
|
+
const hash1 = await computeLeafHash(mockHash("span1"));
|
|
267
|
+
const hash2 = await computeLeafHash(mockHash("span2"));
|
|
268
|
+
expect(hash1).not.toBe(hash2);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe("computeNodeHash", () => {
|
|
273
|
+
it("produces consistent output for same inputs", async () => {
|
|
274
|
+
const left = mockHash("left");
|
|
275
|
+
const right = mockHash("right");
|
|
276
|
+
|
|
277
|
+
const hash1 = await computeNodeHash(left, right);
|
|
278
|
+
const hash2 = await computeNodeHash(left, right);
|
|
279
|
+
|
|
280
|
+
expect(hash1).toBe(hash2);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("produces different output for swapped inputs", async () => {
|
|
284
|
+
const left = mockHash("left");
|
|
285
|
+
const right = mockHash("right");
|
|
286
|
+
|
|
287
|
+
const hash1 = await computeNodeHash(left, right);
|
|
288
|
+
const hash2 = await computeNodeHash(right, left);
|
|
289
|
+
|
|
290
|
+
expect(hash1).not.toBe(hash2);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe("computeMerkleRoot", () => {
|
|
295
|
+
it("returns empty string for empty leaves", async () => {
|
|
296
|
+
const root = await computeMerkleRoot([]);
|
|
297
|
+
expect(root).toBe("");
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("returns leaf hash for single leaf", async () => {
|
|
301
|
+
const leafHash = mockHash("single-leaf");
|
|
302
|
+
const root = await computeMerkleRoot([leafHash]);
|
|
303
|
+
expect(root).toBe(leafHash);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("computes root for two leaves", async () => {
|
|
307
|
+
const leaf1 = mockHash("leaf1");
|
|
308
|
+
const leaf2 = mockHash("leaf2");
|
|
309
|
+
|
|
310
|
+
const root = await computeMerkleRoot([leaf1, leaf2]);
|
|
311
|
+
const expected = await computeNodeHash(leaf1, leaf2);
|
|
312
|
+
|
|
313
|
+
expect(root).toBe(expected);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("computes consistent root for same leaves", async () => {
|
|
317
|
+
const leaves = [mockHash("a"), mockHash("b"), mockHash("c")];
|
|
318
|
+
|
|
319
|
+
const root1 = await computeMerkleRoot(leaves);
|
|
320
|
+
const root2 = await computeMerkleRoot(leaves);
|
|
321
|
+
|
|
322
|
+
expect(root1).toBe(root2);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it("handles odd number of leaves (duplication)", async () => {
|
|
326
|
+
const leaves = [mockHash("a"), mockHash("b"), mockHash("c")];
|
|
327
|
+
const root = await computeMerkleRoot(leaves);
|
|
328
|
+
|
|
329
|
+
expect(root).toHaveLength(64);
|
|
330
|
+
expect(root).toMatch(/^[a-f0-9]+$/);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe("verifyMerkleProof", () => {
|
|
335
|
+
it("verifies single leaf (no siblings)", async () => {
|
|
336
|
+
const leafHash = mockHash("single-leaf");
|
|
337
|
+
const proof: MerkleProof = {
|
|
338
|
+
leafHash,
|
|
339
|
+
leafIndex: 0,
|
|
340
|
+
siblings: [],
|
|
341
|
+
rootHash: leafHash,
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const isValid = await verifyMerkleProof(proof);
|
|
345
|
+
expect(isValid).toBe(true);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("verifies two-leaf tree proof", async () => {
|
|
349
|
+
const leaf1 = mockHash("leaf1");
|
|
350
|
+
const leaf2 = mockHash("leaf2");
|
|
351
|
+
const root = await computeNodeHash(leaf1, leaf2);
|
|
352
|
+
|
|
353
|
+
const proof: MerkleProof = {
|
|
354
|
+
leafHash: leaf1,
|
|
355
|
+
leafIndex: 0,
|
|
356
|
+
siblings: [{ hash: leaf2, position: "right" }],
|
|
357
|
+
rootHash: root,
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const isValid = await verifyMerkleProof(proof);
|
|
361
|
+
expect(isValid).toBe(true);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it("rejects tampered leaf hash", async () => {
|
|
365
|
+
const leaf1 = mockHash("leaf1");
|
|
366
|
+
const leaf2 = mockHash("leaf2");
|
|
367
|
+
const root = await computeNodeHash(leaf1, leaf2);
|
|
368
|
+
|
|
369
|
+
const proof: MerkleProof = {
|
|
370
|
+
leafHash: mockHash("tampered"),
|
|
371
|
+
leafIndex: 0,
|
|
372
|
+
siblings: [{ hash: leaf2, position: "right" }],
|
|
373
|
+
rootHash: root,
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const isValid = await verifyMerkleProof(proof);
|
|
377
|
+
expect(isValid).toBe(false);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("rejects wrong sibling position", async () => {
|
|
381
|
+
const leaf1 = mockHash("leaf1");
|
|
382
|
+
const leaf2 = mockHash("leaf2");
|
|
383
|
+
const root = await computeNodeHash(leaf1, leaf2);
|
|
384
|
+
|
|
385
|
+
const proof: MerkleProof = {
|
|
386
|
+
leafHash: leaf1,
|
|
387
|
+
leafIndex: 0,
|
|
388
|
+
siblings: [{ hash: leaf2, position: "left" }], // Wrong position
|
|
389
|
+
rootHash: root,
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const isValid = await verifyMerkleProof(proof);
|
|
393
|
+
expect(isValid).toBe(false);
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
describe("generateMerkleInclusionProof", () => {
|
|
398
|
+
it("generates proof for single span", async () => {
|
|
399
|
+
const { bundle } = await createTestBundle(1);
|
|
400
|
+
|
|
401
|
+
const result = await generateMerkleInclusionProof(bundle, "span-0");
|
|
402
|
+
|
|
403
|
+
expect(result.spanHash).toHaveLength(64);
|
|
404
|
+
expect(result.leafHash).toHaveLength(64);
|
|
405
|
+
expect(result.merkleProof.siblings).toHaveLength(0);
|
|
406
|
+
expect(result.span.id).toBe("span-0");
|
|
407
|
+
expect(result.events.length).toBeGreaterThan(0);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("generates proof for multi-span bundle", async () => {
|
|
411
|
+
const { bundle, merkleRoot } = await createTestBundle(4);
|
|
412
|
+
|
|
413
|
+
const result = await generateMerkleInclusionProof(bundle, "span-2");
|
|
414
|
+
|
|
415
|
+
expect(result.merkleProof.rootHash).toBe(merkleRoot);
|
|
416
|
+
expect(result.merkleProof.siblings.length).toBeGreaterThan(0);
|
|
417
|
+
expect(result.span.id).toBe("span-2");
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("throws for non-existent span", async () => {
|
|
421
|
+
const { bundle } = await createTestBundle(1);
|
|
422
|
+
|
|
423
|
+
await expect(
|
|
424
|
+
generateMerkleInclusionProof(bundle, "non-existent-span")
|
|
425
|
+
).rejects.toThrow(/Span not found/);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
describe("verifySpanInclusion", () => {
|
|
430
|
+
it("verifies span inclusion in single-span bundle", async () => {
|
|
431
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
432
|
+
const span = bundle.privateRun.spans[0]!;
|
|
433
|
+
const events = bundle.privateRun.events.filter((e) =>
|
|
434
|
+
span.eventIds.includes(e.id)
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
const inclusion = await generateMerkleInclusionProof(bundle, span.id);
|
|
438
|
+
const isValid = await verifySpanInclusion(inclusion.merkleProof, span, events);
|
|
439
|
+
|
|
440
|
+
expect(isValid).toBe(true);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it("rejects tampered span data", async () => {
|
|
444
|
+
const { bundle } = await createTestBundle(1);
|
|
445
|
+
const span = bundle.privateRun.spans[0]!;
|
|
446
|
+
const events = bundle.privateRun.events.filter((e) =>
|
|
447
|
+
span.eventIds.includes(e.id)
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
const inclusion = await generateMerkleInclusionProof(bundle, span.id);
|
|
451
|
+
|
|
452
|
+
// Tamper with span name
|
|
453
|
+
const tamperedSpan = { ...span, name: "tampered" };
|
|
454
|
+
const isValid = await verifySpanInclusion(inclusion.merkleProof, tamperedSpan, events);
|
|
455
|
+
|
|
456
|
+
expect(isValid).toBe(false);
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// =============================================================================
|
|
461
|
+
// PROVER INSTANTIATION TESTS
|
|
462
|
+
// =============================================================================
|
|
463
|
+
|
|
464
|
+
describe("SelectiveDisclosureProver instantiation", () => {
|
|
465
|
+
it("creates prover with default options", () => {
|
|
466
|
+
const prover = new SelectiveDisclosureProver();
|
|
467
|
+
expect(prover).toBeInstanceOf(SelectiveDisclosureProver);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it("creates prover with debug option", () => {
|
|
471
|
+
const prover = new SelectiveDisclosureProver({ debug: true });
|
|
472
|
+
expect(prover).toBeInstanceOf(SelectiveDisclosureProver);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it("creates prover with disclosure options", () => {
|
|
476
|
+
const prover = new SelectiveDisclosureProver({
|
|
477
|
+
includeSpanData: false,
|
|
478
|
+
includeEventData: false,
|
|
479
|
+
});
|
|
480
|
+
expect(prover).toBeInstanceOf(SelectiveDisclosureProver);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it("creates prover using factory function", () => {
|
|
484
|
+
const prover = createSelectiveDisclosureProver();
|
|
485
|
+
expect(prover).toBeInstanceOf(SelectiveDisclosureProver);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
// =============================================================================
|
|
490
|
+
// PROOF GENERATION TESTS
|
|
491
|
+
// =============================================================================
|
|
492
|
+
|
|
493
|
+
describe("SelectiveDisclosureProver.generateProof", () => {
|
|
494
|
+
let prover: SelectiveDisclosureProver;
|
|
495
|
+
|
|
496
|
+
beforeEach(() => {
|
|
497
|
+
prover = new SelectiveDisclosureProver();
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it("generates proof for single-span bundle", async () => {
|
|
501
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
502
|
+
|
|
503
|
+
const input: DisclosureInput = {
|
|
504
|
+
bundle,
|
|
505
|
+
spanId: "span-0",
|
|
506
|
+
merkleRoot,
|
|
507
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const proof = await prover.generateProof(input);
|
|
511
|
+
|
|
512
|
+
expect(proof.proofType).toBe("selective-disclosure");
|
|
513
|
+
expect(proof.proofId).toMatch(/^disclosure-proof-/);
|
|
514
|
+
expect(proof.proof).toBeInstanceOf(Uint8Array);
|
|
515
|
+
expect(proof.proof.length).toBeGreaterThan(67);
|
|
516
|
+
expect(proof.publicInputs.spanHash).toHaveLength(64);
|
|
517
|
+
expect(proof.publicInputs.merkleRoot).toBe(merkleRoot);
|
|
518
|
+
expect(proof.publicInputs.cardanoAnchorTxHash).toBe(input.cardanoAnchorTxHash);
|
|
519
|
+
expect(proof.provingTimeMs).toBeGreaterThanOrEqual(0);
|
|
520
|
+
expect(proof.createdAt).toBeDefined();
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it("generates proof with disclosed span data", async () => {
|
|
524
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
525
|
+
|
|
526
|
+
const input: DisclosureInput = {
|
|
527
|
+
bundle,
|
|
528
|
+
spanId: "span-0",
|
|
529
|
+
merkleRoot,
|
|
530
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
const proof = await prover.generateProof(input);
|
|
534
|
+
|
|
535
|
+
expect(proof.disclosedSpan).toBeDefined();
|
|
536
|
+
expect(proof.disclosedSpan?.id).toBe("span-0");
|
|
537
|
+
expect(proof.disclosedEvents).toBeDefined();
|
|
538
|
+
expect(proof.disclosedEvents?.length).toBeGreaterThan(0);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("generates proof without disclosed data when configured", async () => {
|
|
542
|
+
const prover = new SelectiveDisclosureProver({
|
|
543
|
+
includeSpanData: false,
|
|
544
|
+
includeEventData: false,
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
548
|
+
|
|
549
|
+
const input: DisclosureInput = {
|
|
550
|
+
bundle,
|
|
551
|
+
spanId: "span-0",
|
|
552
|
+
merkleRoot,
|
|
553
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const proof = await prover.generateProof(input);
|
|
557
|
+
|
|
558
|
+
expect(proof.disclosedSpan).toBeUndefined();
|
|
559
|
+
expect(proof.disclosedEvents).toBeUndefined();
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it("generates proof for multi-span bundle", async () => {
|
|
563
|
+
const { bundle, merkleRoot } = await createTestBundle(4);
|
|
564
|
+
|
|
565
|
+
const input: DisclosureInput = {
|
|
566
|
+
bundle,
|
|
567
|
+
spanId: "span-2",
|
|
568
|
+
merkleRoot,
|
|
569
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
const proof = await prover.generateProof(input);
|
|
573
|
+
|
|
574
|
+
expect(proof.proofType).toBe("selective-disclosure");
|
|
575
|
+
expect(proof.publicInputs.merkleRoot).toBe(merkleRoot);
|
|
576
|
+
expect(proof.disclosedSpan?.id).toBe("span-2");
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("throws for mismatched Merkle root", async () => {
|
|
580
|
+
const { bundle } = await createTestBundle(1);
|
|
581
|
+
|
|
582
|
+
const input: DisclosureInput = {
|
|
583
|
+
bundle,
|
|
584
|
+
spanId: "span-0",
|
|
585
|
+
merkleRoot: mockHash("wrong-root"),
|
|
586
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
await expect(prover.generateProof(input)).rejects.toThrow(/does not match expected/);
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
// =============================================================================
|
|
594
|
+
// INPUT VALIDATION TESTS
|
|
595
|
+
// =============================================================================
|
|
596
|
+
|
|
597
|
+
describe("SelectiveDisclosureProver input validation", () => {
|
|
598
|
+
let prover: SelectiveDisclosureProver;
|
|
599
|
+
|
|
600
|
+
beforeEach(() => {
|
|
601
|
+
prover = new SelectiveDisclosureProver();
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it("rejects missing span ID", async () => {
|
|
605
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
606
|
+
|
|
607
|
+
const input: DisclosureInput = {
|
|
608
|
+
bundle,
|
|
609
|
+
spanId: "",
|
|
610
|
+
merkleRoot,
|
|
611
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
await expect(prover.generateProof(input)).rejects.toThrow(/Span ID is required/);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it("rejects invalid Merkle root (too short)", async () => {
|
|
618
|
+
const { bundle } = await createTestBundle(1);
|
|
619
|
+
|
|
620
|
+
const input: DisclosureInput = {
|
|
621
|
+
bundle,
|
|
622
|
+
spanId: "span-0",
|
|
623
|
+
merkleRoot: "abc123",
|
|
624
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
await expect(prover.generateProof(input)).rejects.toThrow(/Invalid Merkle root/);
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it("rejects missing Cardano anchor hash", async () => {
|
|
631
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
632
|
+
|
|
633
|
+
const input: DisclosureInput = {
|
|
634
|
+
bundle,
|
|
635
|
+
spanId: "span-0",
|
|
636
|
+
merkleRoot,
|
|
637
|
+
cardanoAnchorTxHash: "",
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
await expect(prover.generateProof(input)).rejects.toThrow(/Cardano anchor transaction hash is required/);
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it("rejects non-existent span", async () => {
|
|
644
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
645
|
+
|
|
646
|
+
const input: DisclosureInput = {
|
|
647
|
+
bundle,
|
|
648
|
+
spanId: "non-existent",
|
|
649
|
+
merkleRoot,
|
|
650
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
await expect(prover.generateProof(input)).rejects.toThrow(/Span not found/);
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// =============================================================================
|
|
658
|
+
// PROOF VERIFICATION TESTS
|
|
659
|
+
// =============================================================================
|
|
660
|
+
|
|
661
|
+
describe("SelectiveDisclosureProver.verifyProof", () => {
|
|
662
|
+
let prover: SelectiveDisclosureProver;
|
|
663
|
+
|
|
664
|
+
beforeEach(() => {
|
|
665
|
+
prover = new SelectiveDisclosureProver();
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it("verifies a valid proof", async () => {
|
|
669
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
670
|
+
|
|
671
|
+
const input: DisclosureInput = {
|
|
672
|
+
bundle,
|
|
673
|
+
spanId: "span-0",
|
|
674
|
+
merkleRoot,
|
|
675
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
const proof = await prover.generateProof(input);
|
|
679
|
+
const isValid = await prover.verifyProof(proof);
|
|
680
|
+
|
|
681
|
+
expect(isValid).toBe(true);
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it("verifies proof with disclosed span data", async () => {
|
|
685
|
+
const { bundle, merkleRoot } = await createTestBundle(2);
|
|
686
|
+
|
|
687
|
+
const input: DisclosureInput = {
|
|
688
|
+
bundle,
|
|
689
|
+
spanId: "span-1",
|
|
690
|
+
merkleRoot,
|
|
691
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
const proof = await prover.generateProof(input);
|
|
695
|
+
expect(proof.disclosedSpan).toBeDefined();
|
|
696
|
+
expect(await prover.verifyProof(proof)).toBe(true);
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("rejects proof with wrong type", async () => {
|
|
700
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
701
|
+
|
|
702
|
+
const input: DisclosureInput = {
|
|
703
|
+
bundle,
|
|
704
|
+
spanId: "span-0",
|
|
705
|
+
merkleRoot,
|
|
706
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
const proof = await prover.generateProof(input);
|
|
710
|
+
(proof as { proofType: string }).proofType = "hash-chain";
|
|
711
|
+
|
|
712
|
+
expect(await prover.verifyProof(proof)).toBe(false);
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it("rejects proof with empty proof bytes", async () => {
|
|
716
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
717
|
+
|
|
718
|
+
const input: DisclosureInput = {
|
|
719
|
+
bundle,
|
|
720
|
+
spanId: "span-0",
|
|
721
|
+
merkleRoot,
|
|
722
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
const proof = await prover.generateProof(input);
|
|
726
|
+
proof.proof = new Uint8Array(0);
|
|
727
|
+
|
|
728
|
+
expect(await prover.verifyProof(proof)).toBe(false);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it("rejects proof with tampered proof ID", async () => {
|
|
732
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
733
|
+
|
|
734
|
+
const input: DisclosureInput = {
|
|
735
|
+
bundle,
|
|
736
|
+
spanId: "span-0",
|
|
737
|
+
merkleRoot,
|
|
738
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
const proof = await prover.generateProof(input);
|
|
742
|
+
proof.proofId = "tampered-proof-id";
|
|
743
|
+
|
|
744
|
+
expect(await prover.verifyProof(proof)).toBe(false);
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
it("rejects proof with invalid span hash length", async () => {
|
|
748
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
749
|
+
|
|
750
|
+
const input: DisclosureInput = {
|
|
751
|
+
bundle,
|
|
752
|
+
spanId: "span-0",
|
|
753
|
+
merkleRoot,
|
|
754
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
const proof = await prover.generateProof(input);
|
|
758
|
+
proof.publicInputs.spanHash = "short";
|
|
759
|
+
|
|
760
|
+
expect(await prover.verifyProof(proof)).toBe(false);
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
// =============================================================================
|
|
765
|
+
// MEMBERSHIP PROOF TESTS
|
|
766
|
+
// =============================================================================
|
|
767
|
+
|
|
768
|
+
describe("SelectiveDisclosureProver.generateMembershipProof", () => {
|
|
769
|
+
let prover: SelectiveDisclosureProver;
|
|
770
|
+
|
|
771
|
+
beforeEach(() => {
|
|
772
|
+
prover = new SelectiveDisclosureProver();
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it("generates membership-only proof (no disclosed data)", async () => {
|
|
776
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
777
|
+
|
|
778
|
+
const input: DisclosureInput = {
|
|
779
|
+
bundle,
|
|
780
|
+
spanId: "span-0",
|
|
781
|
+
merkleRoot,
|
|
782
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
const proof = await prover.generateMembershipProof(input);
|
|
786
|
+
|
|
787
|
+
expect(proof.proofType).toBe("selective-disclosure");
|
|
788
|
+
expect(proof.disclosedSpan).toBeUndefined();
|
|
789
|
+
expect(proof.disclosedEvents).toBeUndefined();
|
|
790
|
+
expect(proof.publicInputs.spanHash).toHaveLength(64);
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
it("membership proof is verifiable", async () => {
|
|
794
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
795
|
+
|
|
796
|
+
const input: DisclosureInput = {
|
|
797
|
+
bundle,
|
|
798
|
+
spanId: "span-0",
|
|
799
|
+
merkleRoot,
|
|
800
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
const proof = await prover.generateMembershipProof(input);
|
|
804
|
+
const isValid = await prover.verifyProof(proof);
|
|
805
|
+
|
|
806
|
+
expect(isValid).toBe(true);
|
|
807
|
+
});
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
// =============================================================================
|
|
811
|
+
// PROOF METRICS TESTS
|
|
812
|
+
// =============================================================================
|
|
813
|
+
|
|
814
|
+
describe("SelectiveDisclosureProver proof metrics", () => {
|
|
815
|
+
let prover: SelectiveDisclosureProver;
|
|
816
|
+
|
|
817
|
+
beforeEach(() => {
|
|
818
|
+
prover = new SelectiveDisclosureProver();
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
it("includes proving time in proof", async () => {
|
|
822
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
823
|
+
|
|
824
|
+
const input: DisclosureInput = {
|
|
825
|
+
bundle,
|
|
826
|
+
spanId: "span-0",
|
|
827
|
+
merkleRoot,
|
|
828
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
const proof = await prover.generateProof(input);
|
|
832
|
+
|
|
833
|
+
expect(proof.provingTimeMs).toBeGreaterThanOrEqual(0);
|
|
834
|
+
expect(proof.provingTimeMs).toBeLessThan(10000);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it("includes proof size in proof", async () => {
|
|
838
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
839
|
+
|
|
840
|
+
const input: DisclosureInput = {
|
|
841
|
+
bundle,
|
|
842
|
+
spanId: "span-0",
|
|
843
|
+
merkleRoot,
|
|
844
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
const proof = await prover.generateProof(input);
|
|
848
|
+
|
|
849
|
+
expect(proof.proofSizeBytes).toBe(proof.proof.length);
|
|
850
|
+
expect(proof.proofSizeBytes).toBeGreaterThan(67);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
it("includes creation timestamp in proof", async () => {
|
|
854
|
+
const beforeTime = new Date().toISOString();
|
|
855
|
+
const { bundle, merkleRoot } = await createTestBundle(1);
|
|
856
|
+
|
|
857
|
+
const input: DisclosureInput = {
|
|
858
|
+
bundle,
|
|
859
|
+
spanId: "span-0",
|
|
860
|
+
merkleRoot,
|
|
861
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
const proof = await prover.generateProof(input);
|
|
865
|
+
const afterTime = new Date().toISOString();
|
|
866
|
+
|
|
867
|
+
expect(proof.createdAt).toBeDefined();
|
|
868
|
+
expect(proof.createdAt >= beforeTime).toBe(true);
|
|
869
|
+
expect(proof.createdAt <= afterTime).toBe(true);
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// =============================================================================
|
|
874
|
+
// MULTI-SPAN TESTS
|
|
875
|
+
// =============================================================================
|
|
876
|
+
|
|
877
|
+
describe("SelectiveDisclosureProver multi-span scenarios", () => {
|
|
878
|
+
let prover: SelectiveDisclosureProver;
|
|
879
|
+
|
|
880
|
+
beforeEach(() => {
|
|
881
|
+
prover = new SelectiveDisclosureProver();
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
it("generates different proofs for different spans in same bundle", async () => {
|
|
885
|
+
const { bundle, merkleRoot } = await createTestBundle(3);
|
|
886
|
+
|
|
887
|
+
const proof1 = await prover.generateProof({
|
|
888
|
+
bundle,
|
|
889
|
+
spanId: "span-0",
|
|
890
|
+
merkleRoot,
|
|
891
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
const proof2 = await prover.generateProof({
|
|
895
|
+
bundle,
|
|
896
|
+
spanId: "span-1",
|
|
897
|
+
merkleRoot,
|
|
898
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
expect(proof1.publicInputs.spanHash).not.toBe(proof2.publicInputs.spanHash);
|
|
902
|
+
expect(proof1.publicInputs.merkleRoot).toBe(proof2.publicInputs.merkleRoot);
|
|
903
|
+
expect(proof1.proofId).not.toBe(proof2.proofId);
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
it("all spans in bundle can be proven", async () => {
|
|
907
|
+
const { bundle, merkleRoot } = await createTestBundle(5);
|
|
908
|
+
|
|
909
|
+
for (let i = 0; i < 5; i++) {
|
|
910
|
+
const proof = await prover.generateProof({
|
|
911
|
+
bundle,
|
|
912
|
+
spanId: `span-${i}`,
|
|
913
|
+
merkleRoot,
|
|
914
|
+
cardanoAnchorTxHash: "abc123def456789012345678901234567890123456789012345678901234",
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
expect(await prover.verifyProof(proof)).toBe(true);
|
|
918
|
+
expect(proof.disclosedSpan?.id).toBe(`span-${i}`);
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
});
|