@private.me/xcontinuity 2.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 (59) hide show
  1. package/AGENTS.md +123 -0
  2. package/LICENSE.md +26 -0
  3. package/MIGRATING.md +77 -0
  4. package/README.md +601 -0
  5. package/dist/adjudicator.d.ts +75 -0
  6. package/dist/adjudicator.js +184 -0
  7. package/dist/cascade.d.ts +157 -0
  8. package/dist/cascade.js +323 -0
  9. package/dist/chronicle.d.ts +76 -0
  10. package/dist/chronicle.js +173 -0
  11. package/dist/cjs/adjudicator.js +189 -0
  12. package/dist/cjs/cascade.js +328 -0
  13. package/dist/cjs/chronicle.js +178 -0
  14. package/dist/cjs/enforcement.js +108 -0
  15. package/dist/cjs/errors.js +72 -0
  16. package/dist/cjs/index.js +108 -0
  17. package/dist/cjs/memory-runtime.js +129 -0
  18. package/dist/cjs/memory-session.js +134 -0
  19. package/dist/cjs/mission.js +178 -0
  20. package/dist/cjs/package.json +1 -0
  21. package/dist/cjs/provenance.js +192 -0
  22. package/dist/cjs/ratification.js +322 -0
  23. package/dist/cjs/reverse-xorida.js +506 -0
  24. package/dist/cjs/session.js +273 -0
  25. package/dist/cjs/state-serializer.js +300 -0
  26. package/dist/cjs/store-memory.js +33 -0
  27. package/dist/cjs/trust.js +133 -0
  28. package/dist/cjs/types.js +59 -0
  29. package/dist/enforcement.d.ts +40 -0
  30. package/dist/enforcement.js +104 -0
  31. package/dist/errors.d.ts +25 -0
  32. package/dist/errors.js +68 -0
  33. package/dist/index.d.ts +34 -0
  34. package/dist/index.js +43 -0
  35. package/dist/memory-runtime.d.ts +36 -0
  36. package/dist/memory-runtime.js +125 -0
  37. package/dist/memory-session.d.ts +38 -0
  38. package/dist/memory-session.js +97 -0
  39. package/dist/mission.d.ts +68 -0
  40. package/dist/mission.js +172 -0
  41. package/dist/provenance.d.ts +54 -0
  42. package/dist/provenance.js +182 -0
  43. package/dist/ratification.d.ts +113 -0
  44. package/dist/ratification.js +317 -0
  45. package/dist/reverse-xorida.d.ts +174 -0
  46. package/dist/reverse-xorida.js +490 -0
  47. package/dist/session.d.ts +102 -0
  48. package/dist/session.js +269 -0
  49. package/dist/state-serializer.d.ts +37 -0
  50. package/dist/state-serializer.js +294 -0
  51. package/dist/store-memory.d.ts +18 -0
  52. package/dist/store-memory.js +29 -0
  53. package/dist/trust.d.ts +76 -0
  54. package/dist/trust.js +121 -0
  55. package/dist/types.d.ts +320 -0
  56. package/dist/types.js +56 -0
  57. package/llms.txt +43 -0
  58. package/package.json +125 -0
  59. package/share1.dat +0 -0
@@ -0,0 +1,506 @@
1
+ "use strict";
2
+ /**
3
+ * @private.me/xcontinuity — Reverse-XorIDA Core Crypto Layer
4
+ *
5
+ * The novel primitive: exploits XorIDA's linearity over GF(2) to perform
6
+ * incremental state updates at O(delta) cost instead of re-splitting entire state.
7
+ *
8
+ * Key insight: split(A XOR B) = split(A) XOR split(B)
9
+ *
10
+ * Therefore: newShares = oldShares XOR split(oldPadded XOR newPadded)
11
+ *
12
+ * HMAC must be recomputed fresh for the new state (not derived from delta).
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.padForSplit = padForSplit;
16
+ exports.splitState = splitState;
17
+ exports.reconstructState = reconstructState;
18
+ exports.computeDelta = computeDelta;
19
+ exports.applyDeltaShares = applyDeltaShares;
20
+ exports.incrementalUpdate = incrementalUpdate;
21
+ exports.undoDelta = undoDelta;
22
+ exports.branchState = branchState;
23
+ exports.squashDeltas = squashDeltas;
24
+ exports.blindUpdateShare = blindUpdateShare;
25
+ exports.refreshShares = refreshShares;
26
+ exports.blindEqual = blindEqual;
27
+ exports.networkCodeShares = networkCodeShares;
28
+ exports.networkDecodeShare = networkDecodeShare;
29
+ const shared_1 = require("@private.me/shared");
30
+ const crypto_1 = require("@private.me/crypto");
31
+ const types_js_1 = require("./types.js");
32
+ const errors_js_1 = require("./errors.js");
33
+ /**
34
+ * Pad data for XorIDA splitting.
35
+ * Block size = nextOddPrime(n) - 1.
36
+ */
37
+ function padForSplit(data, n) {
38
+ const p = (0, crypto_1.nextOddPrime)(n);
39
+ const blockSize = p - 1;
40
+ return (0, crypto_1.pkcs7Pad)(data, blockSize);
41
+ }
42
+ /**
43
+ * Split a state snapshot into XorIDA threshold shares.
44
+ *
45
+ * Pipeline: pad -> HMAC -> split -> package as StateShare[]
46
+ *
47
+ * @param snapshot - Serialized state with checksum
48
+ * @param config - Split configuration (n, k)
49
+ * @returns SplitState containing all shares
50
+ */
51
+ async function splitState(snapshot, config = types_js_1.DEFAULT_SPLIT_CONFIG) {
52
+ try {
53
+ const { n, k } = config;
54
+ if (n < 2 || k < 2 || k > n) {
55
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('SPLIT_FAILED', `Invalid config: n=${n}, k=${k}`));
56
+ }
57
+ const padded = padForSplit(snapshot.serializedBytes, n);
58
+ const { key: hmacKey, signature: hmacSignature } = await (0, crypto_1.generateHMAC)(padded);
59
+ const rawShares = (0, crypto_1.splitXorIDA)(padded, n, k);
60
+ const shares = rawShares.map((data, index) => ({
61
+ stateId: snapshot.stateId,
62
+ index,
63
+ n,
64
+ k,
65
+ data,
66
+ hmacKey,
67
+ hmacSignature,
68
+ }));
69
+ return (0, shared_1.ok)({
70
+ stateId: snapshot.stateId,
71
+ metadata: snapshot.metadata,
72
+ shares,
73
+ n,
74
+ k,
75
+ paddedLength: padded.length,
76
+ });
77
+ }
78
+ catch (e) {
79
+ const msg = e instanceof Error ? e.message : 'Split failed';
80
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('SPLIT_FAILED', msg));
81
+ }
82
+ }
83
+ /**
84
+ * Reconstruct a state snapshot from threshold shares.
85
+ *
86
+ * Pipeline: validate -> reconstruct -> verify HMAC -> unpad -> return snapshot bytes
87
+ *
88
+ * @param shares - Array of k or more StateShares (uses first k)
89
+ * @returns Reconstructed StateSnapshot serialized bytes and checksum
90
+ */
91
+ async function reconstructState(shares) {
92
+ try {
93
+ if (shares.length === 0) {
94
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('INSUFFICIENT_SHARES', 'No shares provided'));
95
+ }
96
+ const first = shares[0];
97
+ const k = first.k;
98
+ const n = first.n;
99
+ if (shares.length < k) {
100
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('INSUFFICIENT_SHARES', `Need ${k} shares, got ${shares.length}`));
101
+ }
102
+ // Verify all shares have same stateId
103
+ const stateId = first.stateId;
104
+ for (const share of shares) {
105
+ if (share.stateId !== stateId) {
106
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('INVALID_SHARES', 'Share stateId mismatch'));
107
+ }
108
+ }
109
+ // Take first k shares
110
+ const subset = shares.slice(0, k);
111
+ const shareData = subset.map(s => s.data);
112
+ const indices = subset.map(s => s.index);
113
+ const reconstructed = (0, crypto_1.reconstructXorIDA)(shareData, indices, n, k);
114
+ // Verify HMAC using the key from the first share
115
+ const hmacValid = await (0, crypto_1.verifyHMAC)(first.hmacKey, reconstructed, first.hmacSignature);
116
+ if (!hmacValid) {
117
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('HMAC_FAILURE', 'HMAC verification failed after reconstruction'));
118
+ }
119
+ // Unpad
120
+ const p = (0, crypto_1.nextOddPrime)(n);
121
+ const blockSize = p - 1;
122
+ const unpadResult = (0, crypto_1.pkcs7Unpad)(reconstructed, blockSize);
123
+ if (!unpadResult.ok) {
124
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('PADDING_ERROR', 'Failed to remove padding after reconstruction'));
125
+ }
126
+ return (0, shared_1.ok)({ serializedBytes: unpadResult.value, stateId });
127
+ }
128
+ catch (e) {
129
+ const msg = e instanceof Error ? e.message : 'Reconstruction failed';
130
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('RECONSTRUCT_FAILED', msg));
131
+ }
132
+ }
133
+ /**
134
+ * Compute the byte-level delta between two padded state arrays.
135
+ * Both arrays must be the same length.
136
+ *
137
+ * delta[i] = oldPadded[i] XOR newPadded[i]
138
+ */
139
+ function computeDelta(oldPadded, newPadded, fromStateId, toStateId, fromChecksum, toChecksum) {
140
+ if (oldPadded.length !== newPadded.length) {
141
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('DELTA_SIZE_MISMATCH', `Padded sizes differ: ${oldPadded.length} vs ${newPadded.length}`));
142
+ }
143
+ const deltaBytes = new Uint8Array(oldPadded.length);
144
+ for (let i = 0; i < oldPadded.length; i++) {
145
+ deltaBytes[i] = oldPadded[i] ^ newPadded[i];
146
+ }
147
+ return (0, shared_1.ok)({ fromStateId, toStateId, deltaBytes, fromChecksum, toChecksum });
148
+ }
149
+ /**
150
+ * Apply delta shares to old split state via XOR.
151
+ *
152
+ * newShares[i].data = oldShares[i].data XOR deltaShares[i].data
153
+ *
154
+ * @param oldShares - Original shares
155
+ * @param deltaShares - Delta shares (from splitting the delta)
156
+ * @param newStateId - State ID for the new split state
157
+ * @param newMetadata - Metadata for the new state
158
+ * @param newHmacKey - Fresh HMAC key for the new state
159
+ * @param newHmacSignature - Fresh HMAC signature for the new padded state
160
+ */
161
+ function applyDeltaShares(oldShares, deltaShares, newStateId, newHmacKey, newHmacSignature) {
162
+ if (oldShares.length !== deltaShares.length) {
163
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('INVALID_SHARES', `Share count mismatch: ${oldShares.length} vs ${deltaShares.length}`));
164
+ }
165
+ const newShares = [];
166
+ for (let i = 0; i < oldShares.length; i++) {
167
+ const oldShare = oldShares[i];
168
+ const deltaData = deltaShares[i];
169
+ if (oldShare.data.length !== deltaData.length) {
170
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('DELTA_SIZE_MISMATCH', `Share ${i} size mismatch: ${oldShare.data.length} vs ${deltaData.length}`));
171
+ }
172
+ const newData = new Uint8Array(oldShare.data.length);
173
+ for (let j = 0; j < oldShare.data.length; j++) {
174
+ newData[j] = oldShare.data[j] ^ deltaData[j];
175
+ }
176
+ newShares.push({
177
+ stateId: newStateId,
178
+ index: oldShare.index,
179
+ n: oldShare.n,
180
+ k: oldShare.k,
181
+ data: newData,
182
+ hmacKey: newHmacKey,
183
+ hmacSignature: newHmacSignature,
184
+ });
185
+ }
186
+ return (0, shared_1.ok)(newShares);
187
+ }
188
+ /**
189
+ * Perform an incremental state update using Reverse-XorIDA.
190
+ *
191
+ * Instead of re-splitting the entire new state:
192
+ * 1. Pad both old and new to the same block-aligned size
193
+ * 2. Compute delta = oldPadded XOR newPadded
194
+ * 3. Split the delta
195
+ * 4. XOR each old share with the corresponding delta share
196
+ * 5. Recompute HMAC fresh for the new padded state
197
+ *
198
+ * Falls back to fresh split when sizes differ significantly
199
+ * (incremental update only works when padded lengths match).
200
+ *
201
+ * @param oldSplitState - Previous split state
202
+ * @param oldSnapshot - Previous state snapshot
203
+ * @param newSnapshot - New state snapshot
204
+ * @returns Updated SplitState with new shares
205
+ */
206
+ async function incrementalUpdate(oldSplitState, oldSnapshot, newSnapshot) {
207
+ try {
208
+ const { n, k } = oldSplitState;
209
+ // Pad both to the same block boundary
210
+ const oldPadded = padForSplit(oldSnapshot.serializedBytes, n);
211
+ const newPadded = padForSplit(newSnapshot.serializedBytes, n);
212
+ // If padded lengths differ, fall back to fresh split
213
+ if (oldPadded.length !== newPadded.length) {
214
+ return splitState(newSnapshot, { n, k });
215
+ }
216
+ // Compute delta
217
+ const deltaBytes = new Uint8Array(oldPadded.length);
218
+ for (let i = 0; i < oldPadded.length; i++) {
219
+ deltaBytes[i] = oldPadded[i] ^ newPadded[i];
220
+ }
221
+ // Split the delta
222
+ const deltaShares = (0, crypto_1.splitXorIDA)(deltaBytes, n, k);
223
+ // Fresh HMAC for the new padded state
224
+ const { key: newHmacKey, signature: newHmacSignature } = await (0, crypto_1.generateHMAC)(newPadded);
225
+ // XOR old shares with delta shares
226
+ const applyResult = applyDeltaShares(oldSplitState.shares, deltaShares, newSnapshot.stateId, newHmacKey, newHmacSignature);
227
+ if (!applyResult.ok)
228
+ return applyResult;
229
+ return (0, shared_1.ok)({
230
+ stateId: newSnapshot.stateId,
231
+ metadata: newSnapshot.metadata,
232
+ shares: applyResult.value,
233
+ n,
234
+ k,
235
+ paddedLength: newPadded.length,
236
+ });
237
+ }
238
+ catch (e) {
239
+ const msg = e instanceof Error ? e.message : 'Incremental update failed';
240
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('SPLIT_FAILED', msg));
241
+ }
242
+ }
243
+ /* ══════════════════════════════════════════════════════════════════
244
+ * Algebraic Extensions (v2.0.0)
245
+ *
246
+ * All extensions exploit XorIDA's linearity over GF(2):
247
+ * split(A ⊕ B) = split(A) ⊕ split(B)
248
+ *
249
+ * This property enables O(δ) operations on encrypted shares
250
+ * without ever reconstructing the plaintext.
251
+ * ══════════════════════════════════════════════════════════════════ */
252
+ /**
253
+ * Extension 1: Undo / Time-Travel
254
+ *
255
+ * Reverse a delta by re-applying it (XOR is its own inverse in GF(2)).
256
+ * Given shares at state B and the delta A→B, produces shares at state A.
257
+ *
258
+ * Math: shares_A = shares_B ⊕ deltaShares(A→B)
259
+ * because: (A ⊕ Δ) ⊕ Δ = A (XOR self-inverse)
260
+ */
261
+ function undoDelta(currentShares, deltaShares, previousStateId, hmacKey, hmacSignature) {
262
+ // Undo is identical to apply — XOR is self-inverse
263
+ return applyDeltaShares(currentShares, deltaShares, previousStateId, hmacKey, hmacSignature);
264
+ }
265
+ /**
266
+ * Extension 2: Branch
267
+ *
268
+ * Fork a split state into an independent branch by copying shares.
269
+ * The branch can diverge independently from the original.
270
+ *
271
+ * @param source - The split state to branch from
272
+ * @param branchStateId - New stateId for the branch
273
+ * @returns Independent copy of the split state
274
+ */
275
+ function branchState(source, branchStateId) {
276
+ const branchedShares = source.shares.map(share => ({
277
+ stateId: branchStateId,
278
+ index: share.index,
279
+ n: share.n,
280
+ k: share.k,
281
+ data: new Uint8Array(share.data),
282
+ hmacKey: new Uint8Array(share.hmacKey),
283
+ hmacSignature: new Uint8Array(share.hmacSignature),
284
+ }));
285
+ return {
286
+ stateId: branchStateId,
287
+ metadata: source.metadata,
288
+ shares: branchedShares,
289
+ n: source.n,
290
+ k: source.k,
291
+ paddedLength: source.paddedLength,
292
+ };
293
+ }
294
+ /**
295
+ * Extension 3: Delta Squashing
296
+ *
297
+ * Combine multiple sequential deltas into a single delta.
298
+ * Exploits GF(2) associativity: Δ_total = Δ₁ ⊕ Δ₂ ⊕ ... ⊕ Δₙ
299
+ *
300
+ * @param deltas - Ordered array of deltas to squash (must form a chain)
301
+ * @returns Single squashed delta from first.fromStateId to last.toStateId
302
+ */
303
+ function squashDeltas(deltas) {
304
+ if (deltas.length === 0) {
305
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('DELTA_SIZE_MISMATCH', 'No deltas to squash'));
306
+ }
307
+ if (deltas.length === 1) {
308
+ return (0, shared_1.ok)(deltas[0]);
309
+ }
310
+ const first = deltas[0];
311
+ const last = deltas[deltas.length - 1];
312
+ // Verify chain continuity
313
+ for (let i = 1; i < deltas.length; i++) {
314
+ if (deltas[i].fromStateId !== deltas[i - 1].toStateId) {
315
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('DELTA_SIZE_MISMATCH', `Delta chain broken at index ${i}: expected from=${deltas[i - 1].toStateId}, got from=${deltas[i].fromStateId}`));
316
+ }
317
+ }
318
+ // Verify all deltas have the same length
319
+ const len = first.deltaBytes.length;
320
+ for (const d of deltas) {
321
+ if (d.deltaBytes.length !== len) {
322
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('DELTA_SIZE_MISMATCH', `Delta size mismatch: expected ${len}, got ${d.deltaBytes.length}`));
323
+ }
324
+ }
325
+ // XOR all deltas together
326
+ const squashed = new Uint8Array(len);
327
+ for (const d of deltas) {
328
+ for (let i = 0; i < len; i++) {
329
+ squashed[i] ^= d.deltaBytes[i];
330
+ }
331
+ }
332
+ return (0, shared_1.ok)({
333
+ fromStateId: first.fromStateId,
334
+ toStateId: last.toStateId,
335
+ deltaBytes: squashed,
336
+ fromChecksum: first.fromChecksum,
337
+ toChecksum: last.toChecksum,
338
+ });
339
+ }
340
+ /**
341
+ * Extension 4: Blind Update
342
+ *
343
+ * Apply a delta to shares without seeing the plaintext state.
344
+ * The share holder never reconstructs the full state.
345
+ *
346
+ * Math: newShare_i = oldShare_i ⊕ deltaShare_i
347
+ *
348
+ * @param share - A single share to update
349
+ * @param deltaShareData - The corresponding delta share data
350
+ * @param newStateId - New state identifier
351
+ * @param newHmacKey - Fresh HMAC key
352
+ * @param newHmacSignature - Fresh HMAC signature
353
+ */
354
+ function blindUpdateShare(share, deltaShareData, newStateId, newHmacKey, newHmacSignature) {
355
+ if (share.data.length !== deltaShareData.length) {
356
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('DELTA_SIZE_MISMATCH', `Share data length ${share.data.length} != delta length ${deltaShareData.length}`));
357
+ }
358
+ const newData = new Uint8Array(share.data.length);
359
+ for (let i = 0; i < share.data.length; i++) {
360
+ newData[i] = share.data[i] ^ deltaShareData[i];
361
+ }
362
+ return (0, shared_1.ok)({
363
+ stateId: newStateId,
364
+ index: share.index,
365
+ n: share.n,
366
+ k: share.k,
367
+ data: newData,
368
+ hmacKey: newHmacKey,
369
+ hmacSignature: newHmacSignature,
370
+ });
371
+ }
372
+ /**
373
+ * Extension 5: Proactive Share Refresh
374
+ *
375
+ * Re-randomize shares while preserving reconstruction correctness.
376
+ * Generates a random mask, splits it, XORs with existing shares.
377
+ * The mask XORs to zero upon reconstruction, so the plaintext is unchanged.
378
+ *
379
+ * @param splitState - Current split state
380
+ * @returns Refreshed split state with re-randomized shares (same plaintext)
381
+ */
382
+ async function refreshShares(state) {
383
+ try {
384
+ const { n, k, paddedLength } = state;
385
+ // Generate random mask of the same padded length
386
+ const mask = new Uint8Array(paddedLength);
387
+ crypto.getRandomValues(mask);
388
+ // Split the mask
389
+ const maskShares = (0, crypto_1.splitXorIDA)(mask, n, k);
390
+ // XOR each share with its mask share
391
+ const refreshedShares = [];
392
+ for (let i = 0; i < state.shares.length; i++) {
393
+ const share = state.shares[i];
394
+ const maskData = maskShares[i];
395
+ const newData = new Uint8Array(share.data.length);
396
+ for (let j = 0; j < share.data.length; j++) {
397
+ newData[j] = share.data[j] ^ maskData[j];
398
+ }
399
+ refreshedShares.push({
400
+ ...share,
401
+ data: newData,
402
+ });
403
+ }
404
+ // Recompute HMAC for the refreshed state
405
+ // Note: the plaintext doesn't change, so we reconstruct and verify
406
+ const refreshedData = refreshedShares.map(s => s.data);
407
+ const indices = refreshedShares.map(s => s.index);
408
+ const reconstructed = (0, crypto_1.reconstructXorIDA)(refreshedData.slice(0, k), indices.slice(0, k), n, k);
409
+ const { key: newHmacKey, signature: newHmacSignature } = await (0, crypto_1.generateHMAC)(reconstructed);
410
+ const finalShares = refreshedShares.map(s => ({
411
+ ...s,
412
+ hmacKey: newHmacKey,
413
+ hmacSignature: newHmacSignature,
414
+ }));
415
+ return (0, shared_1.ok)({
416
+ ...state,
417
+ shares: finalShares,
418
+ });
419
+ }
420
+ catch (e) {
421
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('SPLIT_FAILED', `Share refresh failed: ${e instanceof Error ? e.message : String(e)}`));
422
+ }
423
+ }
424
+ /**
425
+ * Extension 6: Blind Equality Check
426
+ *
427
+ * Compare two split states for equality without reconstructing plaintext.
428
+ * Computes delta between corresponding shares — if all delta bytes are zero,
429
+ * the states are equal.
430
+ *
431
+ * @param a - First split state
432
+ * @param b - Second split state
433
+ * @returns true if both states encode the same plaintext
434
+ */
435
+ function blindEqual(a, b) {
436
+ if (a.n !== b.n || a.k !== b.k) {
437
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('INVALID_SHARES', 'Split configs differ'));
438
+ }
439
+ if (a.shares.length !== b.shares.length) {
440
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('INVALID_SHARES', 'Share count differs'));
441
+ }
442
+ // Check if all corresponding share data XOR to zero
443
+ for (let i = 0; i < a.shares.length; i++) {
444
+ const aShare = a.shares[i];
445
+ const bShare = b.shares[i];
446
+ if (aShare.data.length !== bShare.data.length) {
447
+ return (0, shared_1.ok)(false);
448
+ }
449
+ for (let j = 0; j < aShare.data.length; j++) {
450
+ if ((aShare.data[j] ^ bShare.data[j]) !== 0) {
451
+ return (0, shared_1.ok)(false);
452
+ }
453
+ }
454
+ }
455
+ return (0, shared_1.ok)(true);
456
+ }
457
+ /**
458
+ * Extension 7: Network-Coded Sync
459
+ *
460
+ * Combine multiple shares during transit using XOR for bandwidth efficiency.
461
+ * The receiver can extract individual shares if they hold the complementary shares.
462
+ *
463
+ * coded = share_i ⊕ share_j
464
+ * share_i = coded ⊕ share_j (if receiver already holds share_j)
465
+ *
466
+ * @param shares - Array of shares to combine
467
+ * @returns Single coded block (XOR of all input shares)
468
+ */
469
+ function networkCodeShares(shares) {
470
+ if (shares.length === 0) {
471
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('INSUFFICIENT_SHARES', 'No shares to code'));
472
+ }
473
+ const len = shares[0].data.length;
474
+ for (const s of shares) {
475
+ if (s.data.length !== len) {
476
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('DELTA_SIZE_MISMATCH', 'Share data lengths differ'));
477
+ }
478
+ }
479
+ const coded = new Uint8Array(len);
480
+ for (const s of shares) {
481
+ for (let i = 0; i < len; i++) {
482
+ coded[i] ^= s.data[i];
483
+ }
484
+ }
485
+ return (0, shared_1.ok)(coded);
486
+ }
487
+ /**
488
+ * Decode a network-coded block using a held share.
489
+ *
490
+ * If coded = share_i ⊕ share_j, and you hold share_j:
491
+ * share_i = coded ⊕ share_j
492
+ *
493
+ * @param codedBlock - The network-coded block
494
+ * @param heldShare - The share the receiver already holds
495
+ * @returns The extracted share data
496
+ */
497
+ function networkDecodeShare(codedBlock, heldShare) {
498
+ if (codedBlock.length !== heldShare.data.length) {
499
+ return (0, shared_1.err)((0, errors_js_1.continuityError)('DELTA_SIZE_MISMATCH', 'Coded block and share lengths differ'));
500
+ }
501
+ const decoded = new Uint8Array(codedBlock.length);
502
+ for (let i = 0; i < codedBlock.length; i++) {
503
+ decoded[i] = codedBlock[i] ^ heldShare.data[i];
504
+ }
505
+ return (0, shared_1.ok)(decoded);
506
+ }