@fluxpointstudios/orynq-sdk-midnight-prover 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +78 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +89 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/linking/cardano-anchor-link.d.ts +250 -0
  7. package/dist/linking/cardano-anchor-link.d.ts.map +1 -0
  8. package/dist/linking/cardano-anchor-link.js +447 -0
  9. package/dist/linking/cardano-anchor-link.js.map +1 -0
  10. package/dist/linking/index.d.ts +33 -0
  11. package/dist/linking/index.d.ts.map +1 -0
  12. package/dist/linking/index.js +31 -0
  13. package/dist/linking/index.js.map +1 -0
  14. package/dist/linking/proof-publication.d.ts +217 -0
  15. package/dist/linking/proof-publication.d.ts.map +1 -0
  16. package/dist/linking/proof-publication.js +385 -0
  17. package/dist/linking/proof-publication.js.map +1 -0
  18. package/dist/midnight/index.d.ts +30 -0
  19. package/dist/midnight/index.d.ts.map +1 -0
  20. package/dist/midnight/index.js +27 -0
  21. package/dist/midnight/index.js.map +1 -0
  22. package/dist/midnight/proof-server-client.d.ts +236 -0
  23. package/dist/midnight/proof-server-client.d.ts.map +1 -0
  24. package/dist/midnight/proof-server-client.js +422 -0
  25. package/dist/midnight/proof-server-client.js.map +1 -0
  26. package/dist/midnight/public-inputs.d.ts +134 -0
  27. package/dist/midnight/public-inputs.d.ts.map +1 -0
  28. package/dist/midnight/public-inputs.js +338 -0
  29. package/dist/midnight/public-inputs.js.map +1 -0
  30. package/dist/midnight/witness-builder.d.ts +119 -0
  31. package/dist/midnight/witness-builder.d.ts.map +1 -0
  32. package/dist/midnight/witness-builder.js +238 -0
  33. package/dist/midnight/witness-builder.js.map +1 -0
  34. package/dist/proofs/hash-chain-proof.d.ts +171 -0
  35. package/dist/proofs/hash-chain-proof.d.ts.map +1 -0
  36. package/dist/proofs/hash-chain-proof.js +437 -0
  37. package/dist/proofs/hash-chain-proof.js.map +1 -0
  38. package/dist/proofs/index.d.ts +35 -0
  39. package/dist/proofs/index.d.ts.map +1 -0
  40. package/dist/proofs/index.js +34 -0
  41. package/dist/proofs/index.js.map +1 -0
  42. package/dist/proofs/policy-compliance-proof.d.ts +165 -0
  43. package/dist/proofs/policy-compliance-proof.d.ts.map +1 -0
  44. package/dist/proofs/policy-compliance-proof.js +514 -0
  45. package/dist/proofs/policy-compliance-proof.js.map +1 -0
  46. package/dist/proofs/selective-disclosure.d.ts +213 -0
  47. package/dist/proofs/selective-disclosure.d.ts.map +1 -0
  48. package/dist/proofs/selective-disclosure.js +629 -0
  49. package/dist/proofs/selective-disclosure.js.map +1 -0
  50. package/dist/prover-interface.d.ts +288 -0
  51. package/dist/prover-interface.d.ts.map +1 -0
  52. package/dist/prover-interface.js +114 -0
  53. package/dist/prover-interface.js.map +1 -0
  54. package/dist/prover.d.ts +163 -0
  55. package/dist/prover.d.ts.map +1 -0
  56. package/dist/prover.js +417 -0
  57. package/dist/prover.js.map +1 -0
  58. package/dist/types.d.ts +410 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +128 -0
  61. package/dist/types.js.map +1 -0
  62. package/package.json +59 -0
  63. package/src/__tests__/hash-chain-proof.test.ts +709 -0
  64. package/src/__tests__/midnight-prover.test.ts +716 -0
  65. package/src/__tests__/policy-compliance.test.ts +567 -0
  66. package/src/__tests__/proof-publication.test.ts +644 -0
  67. package/src/__tests__/selective-disclosure.test.ts +921 -0
  68. package/src/index.ts +260 -0
  69. package/src/linking/cardano-anchor-link.ts +682 -0
  70. package/src/linking/index.ts +58 -0
  71. package/src/linking/proof-publication.ts +557 -0
  72. package/src/midnight/index.ts +73 -0
  73. package/src/midnight/proof-server-client.ts +595 -0
  74. package/src/midnight/public-inputs.ts +590 -0
  75. package/src/midnight/witness-builder.ts +341 -0
  76. package/src/proofs/hash-chain-proof.ts +610 -0
  77. package/src/proofs/index.ts +77 -0
  78. package/src/proofs/policy-compliance-proof.ts +717 -0
  79. package/src/proofs/selective-disclosure.ts +839 -0
  80. package/src/prover-interface.ts +410 -0
  81. package/src/prover.ts +537 -0
  82. package/src/types.ts +551 -0
@@ -0,0 +1,341 @@
1
+ /**
2
+ * @fileoverview Witness builder for hash-chain ZK proofs.
3
+ *
4
+ * Location: packages/midnight-prover/src/midnight/witness-builder.ts
5
+ *
6
+ * Summary:
7
+ * This module converts trace events into a circuit-compatible witness format
8
+ * for the hash-chain validity proof. The witness contains the private inputs
9
+ * needed by the ZK circuit to prove that a sequence of events produces
10
+ * the expected rolling hash.
11
+ *
12
+ * Usage:
13
+ * Used by HashChainProver to prepare data for proof generation.
14
+ * The witness format matches the expected Compact circuit input structure.
15
+ *
16
+ * Related files:
17
+ * - hash-chain-proof.ts: Uses this to build witnesses for proof generation
18
+ * - public-inputs.ts: Builds corresponding public inputs
19
+ * - @fluxpointstudios/orynq-sdk-process-trace: TraceEvent types
20
+ */
21
+
22
+ import type { TraceEvent } from "@fluxpointstudios/orynq-sdk-process-trace";
23
+ import {
24
+ sha256StringHex,
25
+ canonicalize,
26
+ hexToBytes,
27
+ } from "@fluxpointstudios/orynq-sdk-core/utils";
28
+
29
+ // =============================================================================
30
+ // TYPES
31
+ // =============================================================================
32
+
33
+ /**
34
+ * Hash domain prefix for event hashing in witness building.
35
+ * Matches the process-trace package domain prefix for consistency.
36
+ */
37
+ const HASH_DOMAIN_EVENT = "poi-trace:event:v1|";
38
+
39
+ /**
40
+ * Hash domain prefix for rolling hash computation.
41
+ * Matches the process-trace package domain prefix for consistency.
42
+ */
43
+ const HASH_DOMAIN_ROLL = "poi-trace:roll:v1|";
44
+
45
+ /**
46
+ * Witness for a single event in the hash chain.
47
+ * Contains both the serialized event data and its computed hash.
48
+ */
49
+ export interface EventWitness {
50
+ /**
51
+ * Sequence number (for ordering verification).
52
+ */
53
+ seq: number;
54
+
55
+ /**
56
+ * Serialized event data as bytes (canonical JSON).
57
+ */
58
+ eventData: Uint8Array;
59
+
60
+ /**
61
+ * SHA-256 hash of the event (domain-separated).
62
+ */
63
+ eventHash: string;
64
+ }
65
+
66
+ /**
67
+ * Complete witness for hash-chain proof.
68
+ * Contains all private inputs needed by the ZK circuit.
69
+ */
70
+ export interface HashChainWitness {
71
+ /**
72
+ * Genesis hash (initial state of the rolling hash).
73
+ */
74
+ genesisHash: string;
75
+
76
+ /**
77
+ * Array of event witnesses in sequence order.
78
+ */
79
+ events: EventWitness[];
80
+
81
+ /**
82
+ * Computed rolling hash after processing all events.
83
+ * Used for internal verification before submitting to circuit.
84
+ */
85
+ computedRollingHash: string;
86
+
87
+ /**
88
+ * Total number of events in the chain.
89
+ */
90
+ eventCount: number;
91
+ }
92
+
93
+ // =============================================================================
94
+ // MAIN FUNCTION
95
+ // =============================================================================
96
+
97
+ /**
98
+ * Build a hash-chain witness from trace events.
99
+ *
100
+ * This function converts an array of TraceEvents into a circuit-compatible
101
+ * witness format. The witness contains:
102
+ * 1. The genesis hash (starting point)
103
+ * 2. Each event serialized as bytes with its hash
104
+ * 3. The computed rolling hash for verification
105
+ *
106
+ * The rolling hash computation matches the algorithm in process-trace:
107
+ * - Events are sorted by seq number
108
+ * - Each event hash is H(domain + canonical(event - hash field))
109
+ * - Rolling hash is H(domain + prevHash + "|" + eventHash)
110
+ *
111
+ * @param events - Array of trace events to build witness from
112
+ * @param genesisHash - Initial hash state (hex string, typically from initRollingHash)
113
+ * @returns Promise resolving to the complete hash chain witness
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const witness = await buildHashChainWitness(traceBundle.privateRun.events, genesisHash);
118
+ * console.log(witness.eventCount); // Number of events
119
+ * console.log(witness.computedRollingHash); // Final hash
120
+ * ```
121
+ */
122
+ export async function buildHashChainWitness(
123
+ events: TraceEvent[],
124
+ genesisHash: string
125
+ ): Promise<HashChainWitness> {
126
+ // Sort events by sequence number for deterministic ordering
127
+ const sortedEvents = [...events].sort((a, b) => a.seq - b.seq);
128
+
129
+ // Build event witnesses
130
+ const eventWitnesses: EventWitness[] = [];
131
+
132
+ for (const event of sortedEvents) {
133
+ const eventWitness = await buildEventWitness(event);
134
+ eventWitnesses.push(eventWitness);
135
+ }
136
+
137
+ // Compute rolling hash
138
+ const computedRollingHash = await computeRollingHashFromWitness(
139
+ genesisHash,
140
+ eventWitnesses
141
+ );
142
+
143
+ return {
144
+ genesisHash,
145
+ events: eventWitnesses,
146
+ computedRollingHash,
147
+ eventCount: eventWitnesses.length,
148
+ };
149
+ }
150
+
151
+ // =============================================================================
152
+ // HELPER FUNCTIONS
153
+ // =============================================================================
154
+
155
+ /**
156
+ * Build a witness for a single event.
157
+ *
158
+ * @param event - The trace event to convert
159
+ * @returns Promise resolving to the event witness
160
+ */
161
+ async function buildEventWitness(event: TraceEvent): Promise<EventWitness> {
162
+ // Remove the hash field to avoid circularity
163
+ const eventWithoutHash = removeHashField(event);
164
+
165
+ // Canonicalize for deterministic serialization
166
+ const canonical = canonicalize(eventWithoutHash);
167
+
168
+ // Convert to bytes for circuit
169
+ const eventData = new TextEncoder().encode(canonical);
170
+
171
+ // Compute hash with domain separation
172
+ const prefixedData = HASH_DOMAIN_EVENT + canonical;
173
+ const eventHash = await sha256StringHex(prefixedData);
174
+
175
+ return {
176
+ seq: event.seq,
177
+ eventData,
178
+ eventHash,
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Compute rolling hash from event witnesses.
184
+ * Matches the algorithm in process-trace/rolling-hash.ts
185
+ *
186
+ * @param genesisHash - Initial hash state
187
+ * @param eventWitnesses - Array of event witnesses
188
+ * @returns Promise resolving to the final rolling hash
189
+ */
190
+ async function computeRollingHashFromWitness(
191
+ genesisHash: string,
192
+ eventWitnesses: EventWitness[]
193
+ ): Promise<string> {
194
+ let currentHash = genesisHash;
195
+
196
+ for (const witness of eventWitnesses) {
197
+ // Rolling hash: H(domain + prevHash + "|" + eventHash)
198
+ const input = HASH_DOMAIN_ROLL + currentHash + "|" + witness.eventHash;
199
+ currentHash = await sha256StringHex(input);
200
+ }
201
+
202
+ return currentHash;
203
+ }
204
+
205
+ /**
206
+ * Remove the 'hash' field from an event object.
207
+ * Returns a shallow copy with all fields except 'hash'.
208
+ *
209
+ * @param event - Event to process
210
+ * @returns Event copy without the hash field
211
+ */
212
+ function removeHashField<T extends { hash?: string }>(event: T): Omit<T, "hash"> {
213
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
214
+ const { hash: _, ...rest } = event;
215
+ return rest;
216
+ }
217
+
218
+ // =============================================================================
219
+ // SERIALIZATION UTILITIES
220
+ // =============================================================================
221
+
222
+ /**
223
+ * Serialize a hash chain witness to a compact binary format.
224
+ * Used for transmitting witness data to the proof server.
225
+ *
226
+ * Format:
227
+ * - 4 bytes: event count (uint32 big-endian)
228
+ * - 32 bytes: genesis hash
229
+ * - For each event:
230
+ * - 4 bytes: event data length (uint32 big-endian)
231
+ * - N bytes: event data
232
+ * - 32 bytes: event hash
233
+ *
234
+ * @param witness - The witness to serialize
235
+ * @returns Serialized witness as Uint8Array
236
+ */
237
+ export function serializeWitness(witness: HashChainWitness): Uint8Array {
238
+ // Calculate total size
239
+ let totalSize = 4 + 32; // count + genesis hash
240
+ for (const event of witness.events) {
241
+ totalSize += 4 + event.eventData.length + 32; // length + data + hash
242
+ }
243
+
244
+ const buffer = new Uint8Array(totalSize);
245
+ const view = new DataView(buffer.buffer);
246
+ let offset = 0;
247
+
248
+ // Write event count
249
+ view.setUint32(offset, witness.eventCount, false); // big-endian
250
+ offset += 4;
251
+
252
+ // Write genesis hash
253
+ const genesisBytes = hexToBytes(witness.genesisHash);
254
+ buffer.set(genesisBytes, offset);
255
+ offset += 32;
256
+
257
+ // Write each event
258
+ for (const event of witness.events) {
259
+ // Event data length
260
+ view.setUint32(offset, event.eventData.length, false);
261
+ offset += 4;
262
+
263
+ // Event data
264
+ buffer.set(event.eventData, offset);
265
+ offset += event.eventData.length;
266
+
267
+ // Event hash
268
+ const hashBytes = hexToBytes(event.eventHash);
269
+ buffer.set(hashBytes, offset);
270
+ offset += 32;
271
+ }
272
+
273
+ return buffer;
274
+ }
275
+
276
+ /**
277
+ * Compute the total size of a witness for resource estimation.
278
+ *
279
+ * @param witness - The witness to measure
280
+ * @returns Size in bytes
281
+ */
282
+ export function computeWitnessSize(witness: HashChainWitness): number {
283
+ let size = 4 + 32; // count + genesis hash
284
+ for (const event of witness.events) {
285
+ size += 4 + event.eventData.length + 32;
286
+ }
287
+ return size;
288
+ }
289
+
290
+ /**
291
+ * Validate that a witness is well-formed.
292
+ *
293
+ * @param witness - The witness to validate
294
+ * @returns Array of validation errors (empty if valid)
295
+ */
296
+ export function validateWitness(witness: HashChainWitness): string[] {
297
+ const errors: string[] = [];
298
+
299
+ // Check genesis hash format
300
+ if (!/^[0-9a-f]{64}$/i.test(witness.genesisHash)) {
301
+ errors.push("Invalid genesis hash format (expected 64-char hex string)");
302
+ }
303
+
304
+ // Check event count matches
305
+ if (witness.eventCount !== witness.events.length) {
306
+ errors.push(
307
+ `Event count mismatch: declared ${witness.eventCount}, actual ${witness.events.length}`
308
+ );
309
+ }
310
+
311
+ // Check each event
312
+ for (let i = 0; i < witness.events.length; i++) {
313
+ const event = witness.events[i];
314
+ if (event === undefined) {
315
+ errors.push(`Event at index ${i} is undefined`);
316
+ continue;
317
+ }
318
+
319
+ // Check event hash format
320
+ if (!/^[0-9a-f]{64}$/i.test(event.eventHash)) {
321
+ errors.push(`Event ${i}: Invalid hash format`);
322
+ }
323
+
324
+ // Check event data is not empty
325
+ if (event.eventData.length === 0) {
326
+ errors.push(`Event ${i}: Empty event data`);
327
+ }
328
+
329
+ // Check sequence ordering
330
+ if (i > 0) {
331
+ const prevEvent = witness.events[i - 1];
332
+ if (prevEvent !== undefined && event.seq <= prevEvent.seq) {
333
+ errors.push(
334
+ `Event ${i}: Sequence ${event.seq} not greater than previous ${prevEvent.seq}`
335
+ );
336
+ }
337
+ }
338
+ }
339
+
340
+ return errors;
341
+ }