@izi-noir/sdk 0.1.13 → 0.1.15

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/dist/index.js CHANGED
@@ -120,10 +120,13 @@ var init_R1csBuilder = __esm({
120
120
  // w_0 = 1 is reserved
121
121
  publicIndices = [];
122
122
  privateIndices = [];
123
+ auxWitnessComputations = [];
123
124
  // BN254 scalar field modulus - 1 (for representing -1)
124
125
  // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
125
126
  // -1 mod Fr = Fr - 1
126
127
  NEG_ONE = "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000";
128
+ // Number of bits for range checks (64 bits handles values up to ~18 quintillion)
129
+ COMPARISON_BITS = 64;
127
130
  /**
128
131
  * Build R1CS definition from the parsed circuit
129
132
  */
@@ -136,7 +139,8 @@ var init_R1csBuilder = __esm({
136
139
  num_witnesses: this.nextWitnessIdx,
137
140
  public_inputs: this.publicIndices,
138
141
  private_inputs: this.privateIndices,
139
- constraints: this.constraints
142
+ constraints: this.constraints,
143
+ auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
140
144
  };
141
145
  }
142
146
  /**
@@ -233,13 +237,18 @@ var init_R1csBuilder = __esm({
233
237
  throw new Error(
234
238
  `Arithmetic operator ${operator} must be part of an equality assertion. Use: assert(a ${operator} b == c)`
235
239
  );
236
- case "<":
240
+ case ">=":
241
+ this.processGreaterThanOrEqual(left, right);
242
+ break;
237
243
  case ">":
244
+ this.processGreaterThan(left, right);
245
+ break;
238
246
  case "<=":
239
- case ">=":
240
- throw new Error(
241
- `Comparison operators (${operator}) require range proofs which are not yet supported. Use the Barretenberg backend for comparison operations.`
242
- );
247
+ this.processGreaterThanOrEqual(right, left);
248
+ break;
249
+ case "<":
250
+ this.processGreaterThan(right, left);
251
+ break;
243
252
  case "&":
244
253
  case "|":
245
254
  throw new Error(
@@ -346,6 +355,105 @@ var init_R1csBuilder = __esm({
346
355
  // = c
347
356
  });
348
357
  }
358
+ /**
359
+ * Process greater than or equal: assert(a >= b)
360
+ * Uses bit decomposition to prove that a - b is non-negative.
361
+ *
362
+ * The approach:
363
+ * 1. Create diff = a - b
364
+ * 2. Decompose diff into COMPARISON_BITS bits
365
+ * 3. For each bit: bit_i * (1 - bit_i) = 0 (ensures 0 or 1)
366
+ * 4. Sum of bits * powers of 2 = diff
367
+ *
368
+ * If decomposition succeeds, diff is in [0, 2^COMPARISON_BITS - 1], so a >= b
369
+ */
370
+ processGreaterThanOrEqual(left, right) {
371
+ const leftIdx = this.getOrCreateWitness(left);
372
+ const rightIdx = this.getOrCreateWitness(right);
373
+ const diffIdx = this.nextWitnessIdx++;
374
+ this.auxWitnessComputations.push({
375
+ type: "subtract",
376
+ targetIdx: diffIdx,
377
+ leftIdx,
378
+ rightIdx
379
+ });
380
+ this.constraints.push({
381
+ a: [
382
+ ["0x1", leftIdx],
383
+ [this.NEG_ONE, rightIdx]
384
+ ],
385
+ b: [["0x1", 0]],
386
+ // * 1
387
+ c: [["0x1", diffIdx]]
388
+ });
389
+ this.addBitDecompositionConstraints(diffIdx);
390
+ }
391
+ /**
392
+ * Process greater than: assert(a > b)
393
+ * Equivalent to assert(a - b - 1 >= 0), or assert(a >= b + 1)
394
+ */
395
+ processGreaterThan(left, right) {
396
+ const leftIdx = this.getOrCreateWitness(left);
397
+ const rightIdx = this.getOrCreateWitness(right);
398
+ const diffIdx = this.nextWitnessIdx++;
399
+ this.auxWitnessComputations.push({
400
+ type: "subtract",
401
+ targetIdx: diffIdx,
402
+ leftIdx,
403
+ rightIdx,
404
+ offset: -1
405
+ // For > operator: diff = left - right - 1
406
+ });
407
+ this.constraints.push({
408
+ a: [
409
+ ["0x1", leftIdx],
410
+ [this.NEG_ONE, rightIdx],
411
+ [this.NEG_ONE, 0]
412
+ // -1 (using w_0 = 1)
413
+ ],
414
+ b: [["0x1", 0]],
415
+ // * 1
416
+ c: [["0x1", diffIdx]]
417
+ });
418
+ this.addBitDecompositionConstraints(diffIdx);
419
+ }
420
+ /**
421
+ * Add bit decomposition constraints for a value.
422
+ * Creates COMPARISON_BITS witnesses for the bits and constrains:
423
+ * 1. Each bit is 0 or 1: bit * (1 - bit) = 0
424
+ * 2. Sum of bits * 2^i = value
425
+ */
426
+ addBitDecompositionConstraints(valueIdx) {
427
+ const bitIndices = [];
428
+ for (let i = 0; i < this.COMPARISON_BITS; i++) {
429
+ const bitIdx = this.nextWitnessIdx++;
430
+ bitIndices.push(bitIdx);
431
+ this.constraints.push({
432
+ a: [["0x1", bitIdx]],
433
+ b: [["0x1", bitIdx]],
434
+ c: [["0x1", bitIdx]]
435
+ });
436
+ }
437
+ this.auxWitnessComputations.push({
438
+ type: "bit_decompose",
439
+ targetIdx: bitIndices[0],
440
+ // First bit index (for reference)
441
+ sourceIdx: valueIdx,
442
+ bitIndices,
443
+ numBits: this.COMPARISON_BITS
444
+ });
445
+ const sumTerms = [];
446
+ for (let i = 0; i < this.COMPARISON_BITS; i++) {
447
+ const coeff = (1n << BigInt(i)).toString(16);
448
+ sumTerms.push([`0x${coeff}`, bitIndices[i]]);
449
+ }
450
+ this.constraints.push({
451
+ a: sumTerms,
452
+ b: [["0x1", 0]],
453
+ // * 1
454
+ c: [["0x1", valueIdx]]
455
+ });
456
+ }
349
457
  /**
350
458
  * Process a variable declaration: let x = expr
351
459
  * Creates a new witness for x and adds constraint if needed
@@ -766,6 +874,15 @@ authors = [""]
766
874
  const strVal = String(value);
767
875
  witnessMap[r1csIndex.toString()] = strVal;
768
876
  }
877
+ const r1cs = JSON.parse(circuit.r1csJson);
878
+ if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
879
+ console.log("=== AUXILIARY WITNESS COMPUTATION ===");
880
+ console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
881
+ console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
882
+ this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
883
+ console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
884
+ console.log("=====================================");
885
+ }
769
886
  const witnessJson = JSON.stringify(witnessMap);
770
887
  let provingKey = circuit.provingKey;
771
888
  if (!provingKey) {
@@ -818,6 +935,63 @@ authors = [""]
818
935
  publicInputs.length
819
936
  );
820
937
  }
938
+ /**
939
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
940
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
941
+ */
942
+ computeAuxiliaryWitnesses(witnessMap, computations) {
943
+ const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
944
+ const getWitnessValue = (idx) => {
945
+ if (idx === 0) return 1n;
946
+ const val = witnessMap[idx.toString()];
947
+ if (val === void 0) {
948
+ throw new Error(`Witness w_${idx} not found`);
949
+ }
950
+ if (val.startsWith("0x")) {
951
+ return BigInt(val);
952
+ }
953
+ return BigInt(val);
954
+ };
955
+ const setWitnessValue = (idx, val) => {
956
+ const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
957
+ witnessMap[idx.toString()] = "0x" + normalized.toString(16);
958
+ };
959
+ for (const comp of computations) {
960
+ switch (comp.type) {
961
+ case "subtract": {
962
+ const left = getWitnessValue(comp.leftIdx);
963
+ const right = getWitnessValue(comp.rightIdx);
964
+ const offset = BigInt(comp.offset ?? 0);
965
+ const result = left - right + offset;
966
+ setWitnessValue(comp.targetIdx, result);
967
+ break;
968
+ }
969
+ case "bit_decompose": {
970
+ const source = getWitnessValue(comp.sourceIdx);
971
+ const bitIndices = comp.bitIndices;
972
+ const numBits = comp.numBits;
973
+ if (source < 0n) {
974
+ throw new Error(
975
+ `Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
976
+ );
977
+ }
978
+ const maxVal = (1n << BigInt(numBits)) - 1n;
979
+ if (source > maxVal) {
980
+ throw new Error(
981
+ `Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
982
+ );
983
+ }
984
+ for (let i = 0; i < numBits; i++) {
985
+ const bit = source >> BigInt(i) & 1n;
986
+ setWitnessValue(bitIndices[i], bit);
987
+ }
988
+ break;
989
+ }
990
+ default:
991
+ throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
992
+ }
993
+ }
994
+ }
821
995
  /**
822
996
  * Get the verifying key in gnark format for on-chain deployment
823
997
  */
@@ -45,10 +45,13 @@ var init_R1csBuilder = __esm({
45
45
  // w_0 = 1 is reserved
46
46
  publicIndices = [];
47
47
  privateIndices = [];
48
+ auxWitnessComputations = [];
48
49
  // BN254 scalar field modulus - 1 (for representing -1)
49
50
  // Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
50
51
  // -1 mod Fr = Fr - 1
51
52
  NEG_ONE = "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000";
53
+ // Number of bits for range checks (64 bits handles values up to ~18 quintillion)
54
+ COMPARISON_BITS = 64;
52
55
  /**
53
56
  * Build R1CS definition from the parsed circuit
54
57
  */
@@ -61,7 +64,8 @@ var init_R1csBuilder = __esm({
61
64
  num_witnesses: this.nextWitnessIdx,
62
65
  public_inputs: this.publicIndices,
63
66
  private_inputs: this.privateIndices,
64
- constraints: this.constraints
67
+ constraints: this.constraints,
68
+ auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
65
69
  };
66
70
  }
67
71
  /**
@@ -158,13 +162,18 @@ var init_R1csBuilder = __esm({
158
162
  throw new Error(
159
163
  `Arithmetic operator ${operator} must be part of an equality assertion. Use: assert(a ${operator} b == c)`
160
164
  );
161
- case "<":
165
+ case ">=":
166
+ this.processGreaterThanOrEqual(left, right);
167
+ break;
162
168
  case ">":
169
+ this.processGreaterThan(left, right);
170
+ break;
163
171
  case "<=":
164
- case ">=":
165
- throw new Error(
166
- `Comparison operators (${operator}) require range proofs which are not yet supported. Use the Barretenberg backend for comparison operations.`
167
- );
172
+ this.processGreaterThanOrEqual(right, left);
173
+ break;
174
+ case "<":
175
+ this.processGreaterThan(right, left);
176
+ break;
168
177
  case "&":
169
178
  case "|":
170
179
  throw new Error(
@@ -271,6 +280,105 @@ var init_R1csBuilder = __esm({
271
280
  // = c
272
281
  });
273
282
  }
283
+ /**
284
+ * Process greater than or equal: assert(a >= b)
285
+ * Uses bit decomposition to prove that a - b is non-negative.
286
+ *
287
+ * The approach:
288
+ * 1. Create diff = a - b
289
+ * 2. Decompose diff into COMPARISON_BITS bits
290
+ * 3. For each bit: bit_i * (1 - bit_i) = 0 (ensures 0 or 1)
291
+ * 4. Sum of bits * powers of 2 = diff
292
+ *
293
+ * If decomposition succeeds, diff is in [0, 2^COMPARISON_BITS - 1], so a >= b
294
+ */
295
+ processGreaterThanOrEqual(left, right) {
296
+ const leftIdx = this.getOrCreateWitness(left);
297
+ const rightIdx = this.getOrCreateWitness(right);
298
+ const diffIdx = this.nextWitnessIdx++;
299
+ this.auxWitnessComputations.push({
300
+ type: "subtract",
301
+ targetIdx: diffIdx,
302
+ leftIdx,
303
+ rightIdx
304
+ });
305
+ this.constraints.push({
306
+ a: [
307
+ ["0x1", leftIdx],
308
+ [this.NEG_ONE, rightIdx]
309
+ ],
310
+ b: [["0x1", 0]],
311
+ // * 1
312
+ c: [["0x1", diffIdx]]
313
+ });
314
+ this.addBitDecompositionConstraints(diffIdx);
315
+ }
316
+ /**
317
+ * Process greater than: assert(a > b)
318
+ * Equivalent to assert(a - b - 1 >= 0), or assert(a >= b + 1)
319
+ */
320
+ processGreaterThan(left, right) {
321
+ const leftIdx = this.getOrCreateWitness(left);
322
+ const rightIdx = this.getOrCreateWitness(right);
323
+ const diffIdx = this.nextWitnessIdx++;
324
+ this.auxWitnessComputations.push({
325
+ type: "subtract",
326
+ targetIdx: diffIdx,
327
+ leftIdx,
328
+ rightIdx,
329
+ offset: -1
330
+ // For > operator: diff = left - right - 1
331
+ });
332
+ this.constraints.push({
333
+ a: [
334
+ ["0x1", leftIdx],
335
+ [this.NEG_ONE, rightIdx],
336
+ [this.NEG_ONE, 0]
337
+ // -1 (using w_0 = 1)
338
+ ],
339
+ b: [["0x1", 0]],
340
+ // * 1
341
+ c: [["0x1", diffIdx]]
342
+ });
343
+ this.addBitDecompositionConstraints(diffIdx);
344
+ }
345
+ /**
346
+ * Add bit decomposition constraints for a value.
347
+ * Creates COMPARISON_BITS witnesses for the bits and constrains:
348
+ * 1. Each bit is 0 or 1: bit * (1 - bit) = 0
349
+ * 2. Sum of bits * 2^i = value
350
+ */
351
+ addBitDecompositionConstraints(valueIdx) {
352
+ const bitIndices = [];
353
+ for (let i = 0; i < this.COMPARISON_BITS; i++) {
354
+ const bitIdx = this.nextWitnessIdx++;
355
+ bitIndices.push(bitIdx);
356
+ this.constraints.push({
357
+ a: [["0x1", bitIdx]],
358
+ b: [["0x1", bitIdx]],
359
+ c: [["0x1", bitIdx]]
360
+ });
361
+ }
362
+ this.auxWitnessComputations.push({
363
+ type: "bit_decompose",
364
+ targetIdx: bitIndices[0],
365
+ // First bit index (for reference)
366
+ sourceIdx: valueIdx,
367
+ bitIndices,
368
+ numBits: this.COMPARISON_BITS
369
+ });
370
+ const sumTerms = [];
371
+ for (let i = 0; i < this.COMPARISON_BITS; i++) {
372
+ const coeff = (1n << BigInt(i)).toString(16);
373
+ sumTerms.push([`0x${coeff}`, bitIndices[i]]);
374
+ }
375
+ this.constraints.push({
376
+ a: sumTerms,
377
+ b: [["0x1", 0]],
378
+ // * 1
379
+ c: [["0x1", valueIdx]]
380
+ });
381
+ }
274
382
  /**
275
383
  * Process a variable declaration: let x = expr
276
384
  * Creates a new witness for x and adds constraint if needed
@@ -692,6 +800,15 @@ authors = [""]
692
800
  const strVal = String(value);
693
801
  witnessMap[r1csIndex.toString()] = strVal;
694
802
  }
803
+ const r1cs = JSON.parse(circuit.r1csJson);
804
+ if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
805
+ console.log("=== AUXILIARY WITNESS COMPUTATION ===");
806
+ console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
807
+ console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
808
+ this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
809
+ console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
810
+ console.log("=====================================");
811
+ }
695
812
  const witnessJson = JSON.stringify(witnessMap);
696
813
  let provingKey = circuit.provingKey;
697
814
  if (!provingKey) {
@@ -744,6 +861,63 @@ authors = [""]
744
861
  publicInputs.length
745
862
  );
746
863
  }
864
+ /**
865
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
866
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
867
+ */
868
+ computeAuxiliaryWitnesses(witnessMap, computations) {
869
+ const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
870
+ const getWitnessValue = (idx) => {
871
+ if (idx === 0) return 1n;
872
+ const val = witnessMap[idx.toString()];
873
+ if (val === void 0) {
874
+ throw new Error(`Witness w_${idx} not found`);
875
+ }
876
+ if (val.startsWith("0x")) {
877
+ return BigInt(val);
878
+ }
879
+ return BigInt(val);
880
+ };
881
+ const setWitnessValue = (idx, val) => {
882
+ const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
883
+ witnessMap[idx.toString()] = "0x" + normalized.toString(16);
884
+ };
885
+ for (const comp of computations) {
886
+ switch (comp.type) {
887
+ case "subtract": {
888
+ const left = getWitnessValue(comp.leftIdx);
889
+ const right = getWitnessValue(comp.rightIdx);
890
+ const offset = BigInt(comp.offset ?? 0);
891
+ const result = left - right + offset;
892
+ setWitnessValue(comp.targetIdx, result);
893
+ break;
894
+ }
895
+ case "bit_decompose": {
896
+ const source = getWitnessValue(comp.sourceIdx);
897
+ const bitIndices = comp.bitIndices;
898
+ const numBits = comp.numBits;
899
+ if (source < 0n) {
900
+ throw new Error(
901
+ `Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
902
+ );
903
+ }
904
+ const maxVal = (1n << BigInt(numBits)) - 1n;
905
+ if (source > maxVal) {
906
+ throw new Error(
907
+ `Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
908
+ );
909
+ }
910
+ for (let i = 0; i < numBits; i++) {
911
+ const bit = source >> BigInt(i) & 1n;
912
+ setWitnessValue(bitIndices[i], bit);
913
+ }
914
+ break;
915
+ }
916
+ default:
917
+ throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
918
+ }
919
+ }
920
+ }
747
921
  /**
748
922
  * Get the verifying key in gnark format for on-chain deployment
749
923
  */
@@ -70,6 +70,7 @@ interface ArkworksWasmConfig {
70
70
  /** Cache proving/verifying keys for repeated proofs */
71
71
  cacheKeys?: boolean;
72
72
  }
73
+
73
74
  /**
74
75
  * Extended CompiledCircuit for ArkworksWasm backend
75
76
  */
@@ -121,6 +122,11 @@ declare class ArkworksWasm implements IProvingSystem {
121
122
  * Verify a Groth16 proof
122
123
  */
123
124
  verifyProof(circuit: CompiledCircuit, proof: Uint8Array, publicInputs: string[]): Promise<boolean>;
125
+ /**
126
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
127
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
128
+ */
129
+ private computeAuxiliaryWitnesses;
124
130
  /**
125
131
  * Get the verifying key in gnark format for on-chain deployment
126
132
  */
@@ -70,6 +70,7 @@ interface ArkworksWasmConfig {
70
70
  /** Cache proving/verifying keys for repeated proofs */
71
71
  cacheKeys?: boolean;
72
72
  }
73
+
73
74
  /**
74
75
  * Extended CompiledCircuit for ArkworksWasm backend
75
76
  */
@@ -121,6 +122,11 @@ declare class ArkworksWasm implements IProvingSystem {
121
122
  * Verify a Groth16 proof
122
123
  */
123
124
  verifyProof(circuit: CompiledCircuit, proof: Uint8Array, publicInputs: string[]): Promise<boolean>;
125
+ /**
126
+ * Compute auxiliary witnesses based on computation instructions from R1csBuilder.
127
+ * This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
128
+ */
129
+ private computeAuxiliaryWitnesses;
124
130
  /**
125
131
  * Get the verifying key in gnark format for on-chain deployment
126
132
  */