@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.cjs +180 -6
- package/dist/index.d.cts +106 -55
- package/dist/index.d.ts +106 -55
- package/dist/index.js +180 -6
- package/dist/providers/arkworks.cjs +180 -6
- package/dist/providers/arkworks.d.cts +6 -0
- package/dist/providers/arkworks.d.ts +6 -0
- package/dist/providers/arkworks.js +180 -6
- package/dist/providers/barretenberg.cjs +180 -6
- package/dist/providers/barretenberg.js +180 -6
- package/package.json +1 -1
|
@@ -23,10 +23,13 @@ var init_R1csBuilder = __esm({
|
|
|
23
23
|
// w_0 = 1 is reserved
|
|
24
24
|
publicIndices = [];
|
|
25
25
|
privateIndices = [];
|
|
26
|
+
auxWitnessComputations = [];
|
|
26
27
|
// BN254 scalar field modulus - 1 (for representing -1)
|
|
27
28
|
// Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
|
|
28
29
|
// -1 mod Fr = Fr - 1
|
|
29
30
|
NEG_ONE = "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000";
|
|
31
|
+
// Number of bits for range checks (64 bits handles values up to ~18 quintillion)
|
|
32
|
+
COMPARISON_BITS = 64;
|
|
30
33
|
/**
|
|
31
34
|
* Build R1CS definition from the parsed circuit
|
|
32
35
|
*/
|
|
@@ -39,7 +42,8 @@ var init_R1csBuilder = __esm({
|
|
|
39
42
|
num_witnesses: this.nextWitnessIdx,
|
|
40
43
|
public_inputs: this.publicIndices,
|
|
41
44
|
private_inputs: this.privateIndices,
|
|
42
|
-
constraints: this.constraints
|
|
45
|
+
constraints: this.constraints,
|
|
46
|
+
auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
|
|
43
47
|
};
|
|
44
48
|
}
|
|
45
49
|
/**
|
|
@@ -136,13 +140,18 @@ var init_R1csBuilder = __esm({
|
|
|
136
140
|
throw new Error(
|
|
137
141
|
`Arithmetic operator ${operator} must be part of an equality assertion. Use: assert(a ${operator} b == c)`
|
|
138
142
|
);
|
|
139
|
-
case "
|
|
143
|
+
case ">=":
|
|
144
|
+
this.processGreaterThanOrEqual(left, right);
|
|
145
|
+
break;
|
|
140
146
|
case ">":
|
|
147
|
+
this.processGreaterThan(left, right);
|
|
148
|
+
break;
|
|
141
149
|
case "<=":
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
);
|
|
150
|
+
this.processGreaterThanOrEqual(right, left);
|
|
151
|
+
break;
|
|
152
|
+
case "<":
|
|
153
|
+
this.processGreaterThan(right, left);
|
|
154
|
+
break;
|
|
146
155
|
case "&":
|
|
147
156
|
case "|":
|
|
148
157
|
throw new Error(
|
|
@@ -249,6 +258,105 @@ var init_R1csBuilder = __esm({
|
|
|
249
258
|
// = c
|
|
250
259
|
});
|
|
251
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Process greater than or equal: assert(a >= b)
|
|
263
|
+
* Uses bit decomposition to prove that a - b is non-negative.
|
|
264
|
+
*
|
|
265
|
+
* The approach:
|
|
266
|
+
* 1. Create diff = a - b
|
|
267
|
+
* 2. Decompose diff into COMPARISON_BITS bits
|
|
268
|
+
* 3. For each bit: bit_i * (1 - bit_i) = 0 (ensures 0 or 1)
|
|
269
|
+
* 4. Sum of bits * powers of 2 = diff
|
|
270
|
+
*
|
|
271
|
+
* If decomposition succeeds, diff is in [0, 2^COMPARISON_BITS - 1], so a >= b
|
|
272
|
+
*/
|
|
273
|
+
processGreaterThanOrEqual(left, right) {
|
|
274
|
+
const leftIdx = this.getOrCreateWitness(left);
|
|
275
|
+
const rightIdx = this.getOrCreateWitness(right);
|
|
276
|
+
const diffIdx = this.nextWitnessIdx++;
|
|
277
|
+
this.auxWitnessComputations.push({
|
|
278
|
+
type: "subtract",
|
|
279
|
+
targetIdx: diffIdx,
|
|
280
|
+
leftIdx,
|
|
281
|
+
rightIdx
|
|
282
|
+
});
|
|
283
|
+
this.constraints.push({
|
|
284
|
+
a: [
|
|
285
|
+
["0x1", leftIdx],
|
|
286
|
+
[this.NEG_ONE, rightIdx]
|
|
287
|
+
],
|
|
288
|
+
b: [["0x1", 0]],
|
|
289
|
+
// * 1
|
|
290
|
+
c: [["0x1", diffIdx]]
|
|
291
|
+
});
|
|
292
|
+
this.addBitDecompositionConstraints(diffIdx);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Process greater than: assert(a > b)
|
|
296
|
+
* Equivalent to assert(a - b - 1 >= 0), or assert(a >= b + 1)
|
|
297
|
+
*/
|
|
298
|
+
processGreaterThan(left, right) {
|
|
299
|
+
const leftIdx = this.getOrCreateWitness(left);
|
|
300
|
+
const rightIdx = this.getOrCreateWitness(right);
|
|
301
|
+
const diffIdx = this.nextWitnessIdx++;
|
|
302
|
+
this.auxWitnessComputations.push({
|
|
303
|
+
type: "subtract",
|
|
304
|
+
targetIdx: diffIdx,
|
|
305
|
+
leftIdx,
|
|
306
|
+
rightIdx,
|
|
307
|
+
offset: -1
|
|
308
|
+
// For > operator: diff = left - right - 1
|
|
309
|
+
});
|
|
310
|
+
this.constraints.push({
|
|
311
|
+
a: [
|
|
312
|
+
["0x1", leftIdx],
|
|
313
|
+
[this.NEG_ONE, rightIdx],
|
|
314
|
+
[this.NEG_ONE, 0]
|
|
315
|
+
// -1 (using w_0 = 1)
|
|
316
|
+
],
|
|
317
|
+
b: [["0x1", 0]],
|
|
318
|
+
// * 1
|
|
319
|
+
c: [["0x1", diffIdx]]
|
|
320
|
+
});
|
|
321
|
+
this.addBitDecompositionConstraints(diffIdx);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Add bit decomposition constraints for a value.
|
|
325
|
+
* Creates COMPARISON_BITS witnesses for the bits and constrains:
|
|
326
|
+
* 1. Each bit is 0 or 1: bit * (1 - bit) = 0
|
|
327
|
+
* 2. Sum of bits * 2^i = value
|
|
328
|
+
*/
|
|
329
|
+
addBitDecompositionConstraints(valueIdx) {
|
|
330
|
+
const bitIndices = [];
|
|
331
|
+
for (let i = 0; i < this.COMPARISON_BITS; i++) {
|
|
332
|
+
const bitIdx = this.nextWitnessIdx++;
|
|
333
|
+
bitIndices.push(bitIdx);
|
|
334
|
+
this.constraints.push({
|
|
335
|
+
a: [["0x1", bitIdx]],
|
|
336
|
+
b: [["0x1", bitIdx]],
|
|
337
|
+
c: [["0x1", bitIdx]]
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
this.auxWitnessComputations.push({
|
|
341
|
+
type: "bit_decompose",
|
|
342
|
+
targetIdx: bitIndices[0],
|
|
343
|
+
// First bit index (for reference)
|
|
344
|
+
sourceIdx: valueIdx,
|
|
345
|
+
bitIndices,
|
|
346
|
+
numBits: this.COMPARISON_BITS
|
|
347
|
+
});
|
|
348
|
+
const sumTerms = [];
|
|
349
|
+
for (let i = 0; i < this.COMPARISON_BITS; i++) {
|
|
350
|
+
const coeff = (1n << BigInt(i)).toString(16);
|
|
351
|
+
sumTerms.push([`0x${coeff}`, bitIndices[i]]);
|
|
352
|
+
}
|
|
353
|
+
this.constraints.push({
|
|
354
|
+
a: sumTerms,
|
|
355
|
+
b: [["0x1", 0]],
|
|
356
|
+
// * 1
|
|
357
|
+
c: [["0x1", valueIdx]]
|
|
358
|
+
});
|
|
359
|
+
}
|
|
252
360
|
/**
|
|
253
361
|
* Process a variable declaration: let x = expr
|
|
254
362
|
* Creates a new witness for x and adds constraint if needed
|
|
@@ -669,6 +777,15 @@ authors = [""]
|
|
|
669
777
|
const strVal = String(value);
|
|
670
778
|
witnessMap[r1csIndex.toString()] = strVal;
|
|
671
779
|
}
|
|
780
|
+
const r1cs = JSON.parse(circuit.r1csJson);
|
|
781
|
+
if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
|
|
782
|
+
console.log("=== AUXILIARY WITNESS COMPUTATION ===");
|
|
783
|
+
console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
|
|
784
|
+
console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
|
|
785
|
+
this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
|
|
786
|
+
console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
|
|
787
|
+
console.log("=====================================");
|
|
788
|
+
}
|
|
672
789
|
const witnessJson = JSON.stringify(witnessMap);
|
|
673
790
|
let provingKey = circuit.provingKey;
|
|
674
791
|
if (!provingKey) {
|
|
@@ -721,6 +838,63 @@ authors = [""]
|
|
|
721
838
|
publicInputs.length
|
|
722
839
|
);
|
|
723
840
|
}
|
|
841
|
+
/**
|
|
842
|
+
* Compute auxiliary witnesses based on computation instructions from R1csBuilder.
|
|
843
|
+
* This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
|
|
844
|
+
*/
|
|
845
|
+
computeAuxiliaryWitnesses(witnessMap, computations) {
|
|
846
|
+
const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
|
|
847
|
+
const getWitnessValue = (idx) => {
|
|
848
|
+
if (idx === 0) return 1n;
|
|
849
|
+
const val = witnessMap[idx.toString()];
|
|
850
|
+
if (val === void 0) {
|
|
851
|
+
throw new Error(`Witness w_${idx} not found`);
|
|
852
|
+
}
|
|
853
|
+
if (val.startsWith("0x")) {
|
|
854
|
+
return BigInt(val);
|
|
855
|
+
}
|
|
856
|
+
return BigInt(val);
|
|
857
|
+
};
|
|
858
|
+
const setWitnessValue = (idx, val) => {
|
|
859
|
+
const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
|
|
860
|
+
witnessMap[idx.toString()] = "0x" + normalized.toString(16);
|
|
861
|
+
};
|
|
862
|
+
for (const comp of computations) {
|
|
863
|
+
switch (comp.type) {
|
|
864
|
+
case "subtract": {
|
|
865
|
+
const left = getWitnessValue(comp.leftIdx);
|
|
866
|
+
const right = getWitnessValue(comp.rightIdx);
|
|
867
|
+
const offset = BigInt(comp.offset ?? 0);
|
|
868
|
+
const result = left - right + offset;
|
|
869
|
+
setWitnessValue(comp.targetIdx, result);
|
|
870
|
+
break;
|
|
871
|
+
}
|
|
872
|
+
case "bit_decompose": {
|
|
873
|
+
const source = getWitnessValue(comp.sourceIdx);
|
|
874
|
+
const bitIndices = comp.bitIndices;
|
|
875
|
+
const numBits = comp.numBits;
|
|
876
|
+
if (source < 0n) {
|
|
877
|
+
throw new Error(
|
|
878
|
+
`Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
const maxVal = (1n << BigInt(numBits)) - 1n;
|
|
882
|
+
if (source > maxVal) {
|
|
883
|
+
throw new Error(
|
|
884
|
+
`Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
for (let i = 0; i < numBits; i++) {
|
|
888
|
+
const bit = source >> BigInt(i) & 1n;
|
|
889
|
+
setWitnessValue(bitIndices[i], bit);
|
|
890
|
+
}
|
|
891
|
+
break;
|
|
892
|
+
}
|
|
893
|
+
default:
|
|
894
|
+
throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
724
898
|
/**
|
|
725
899
|
* Get the verifying key in gnark format for on-chain deployment
|
|
726
900
|
*/
|
|
@@ -142,10 +142,13 @@ var init_R1csBuilder = __esm({
|
|
|
142
142
|
// w_0 = 1 is reserved
|
|
143
143
|
publicIndices = [];
|
|
144
144
|
privateIndices = [];
|
|
145
|
+
auxWitnessComputations = [];
|
|
145
146
|
// BN254 scalar field modulus - 1 (for representing -1)
|
|
146
147
|
// Fr modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
|
|
147
148
|
// -1 mod Fr = Fr - 1
|
|
148
149
|
NEG_ONE = "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000";
|
|
150
|
+
// Number of bits for range checks (64 bits handles values up to ~18 quintillion)
|
|
151
|
+
COMPARISON_BITS = 64;
|
|
149
152
|
/**
|
|
150
153
|
* Build R1CS definition from the parsed circuit
|
|
151
154
|
*/
|
|
@@ -158,7 +161,8 @@ var init_R1csBuilder = __esm({
|
|
|
158
161
|
num_witnesses: this.nextWitnessIdx,
|
|
159
162
|
public_inputs: this.publicIndices,
|
|
160
163
|
private_inputs: this.privateIndices,
|
|
161
|
-
constraints: this.constraints
|
|
164
|
+
constraints: this.constraints,
|
|
165
|
+
auxWitnessComputations: this.auxWitnessComputations.length > 0 ? this.auxWitnessComputations : void 0
|
|
162
166
|
};
|
|
163
167
|
}
|
|
164
168
|
/**
|
|
@@ -255,13 +259,18 @@ var init_R1csBuilder = __esm({
|
|
|
255
259
|
throw new Error(
|
|
256
260
|
`Arithmetic operator ${operator} must be part of an equality assertion. Use: assert(a ${operator} b == c)`
|
|
257
261
|
);
|
|
258
|
-
case "
|
|
262
|
+
case ">=":
|
|
263
|
+
this.processGreaterThanOrEqual(left, right);
|
|
264
|
+
break;
|
|
259
265
|
case ">":
|
|
266
|
+
this.processGreaterThan(left, right);
|
|
267
|
+
break;
|
|
260
268
|
case "<=":
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
);
|
|
269
|
+
this.processGreaterThanOrEqual(right, left);
|
|
270
|
+
break;
|
|
271
|
+
case "<":
|
|
272
|
+
this.processGreaterThan(right, left);
|
|
273
|
+
break;
|
|
265
274
|
case "&":
|
|
266
275
|
case "|":
|
|
267
276
|
throw new Error(
|
|
@@ -368,6 +377,105 @@ var init_R1csBuilder = __esm({
|
|
|
368
377
|
// = c
|
|
369
378
|
});
|
|
370
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Process greater than or equal: assert(a >= b)
|
|
382
|
+
* Uses bit decomposition to prove that a - b is non-negative.
|
|
383
|
+
*
|
|
384
|
+
* The approach:
|
|
385
|
+
* 1. Create diff = a - b
|
|
386
|
+
* 2. Decompose diff into COMPARISON_BITS bits
|
|
387
|
+
* 3. For each bit: bit_i * (1 - bit_i) = 0 (ensures 0 or 1)
|
|
388
|
+
* 4. Sum of bits * powers of 2 = diff
|
|
389
|
+
*
|
|
390
|
+
* If decomposition succeeds, diff is in [0, 2^COMPARISON_BITS - 1], so a >= b
|
|
391
|
+
*/
|
|
392
|
+
processGreaterThanOrEqual(left, right) {
|
|
393
|
+
const leftIdx = this.getOrCreateWitness(left);
|
|
394
|
+
const rightIdx = this.getOrCreateWitness(right);
|
|
395
|
+
const diffIdx = this.nextWitnessIdx++;
|
|
396
|
+
this.auxWitnessComputations.push({
|
|
397
|
+
type: "subtract",
|
|
398
|
+
targetIdx: diffIdx,
|
|
399
|
+
leftIdx,
|
|
400
|
+
rightIdx
|
|
401
|
+
});
|
|
402
|
+
this.constraints.push({
|
|
403
|
+
a: [
|
|
404
|
+
["0x1", leftIdx],
|
|
405
|
+
[this.NEG_ONE, rightIdx]
|
|
406
|
+
],
|
|
407
|
+
b: [["0x1", 0]],
|
|
408
|
+
// * 1
|
|
409
|
+
c: [["0x1", diffIdx]]
|
|
410
|
+
});
|
|
411
|
+
this.addBitDecompositionConstraints(diffIdx);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Process greater than: assert(a > b)
|
|
415
|
+
* Equivalent to assert(a - b - 1 >= 0), or assert(a >= b + 1)
|
|
416
|
+
*/
|
|
417
|
+
processGreaterThan(left, right) {
|
|
418
|
+
const leftIdx = this.getOrCreateWitness(left);
|
|
419
|
+
const rightIdx = this.getOrCreateWitness(right);
|
|
420
|
+
const diffIdx = this.nextWitnessIdx++;
|
|
421
|
+
this.auxWitnessComputations.push({
|
|
422
|
+
type: "subtract",
|
|
423
|
+
targetIdx: diffIdx,
|
|
424
|
+
leftIdx,
|
|
425
|
+
rightIdx,
|
|
426
|
+
offset: -1
|
|
427
|
+
// For > operator: diff = left - right - 1
|
|
428
|
+
});
|
|
429
|
+
this.constraints.push({
|
|
430
|
+
a: [
|
|
431
|
+
["0x1", leftIdx],
|
|
432
|
+
[this.NEG_ONE, rightIdx],
|
|
433
|
+
[this.NEG_ONE, 0]
|
|
434
|
+
// -1 (using w_0 = 1)
|
|
435
|
+
],
|
|
436
|
+
b: [["0x1", 0]],
|
|
437
|
+
// * 1
|
|
438
|
+
c: [["0x1", diffIdx]]
|
|
439
|
+
});
|
|
440
|
+
this.addBitDecompositionConstraints(diffIdx);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Add bit decomposition constraints for a value.
|
|
444
|
+
* Creates COMPARISON_BITS witnesses for the bits and constrains:
|
|
445
|
+
* 1. Each bit is 0 or 1: bit * (1 - bit) = 0
|
|
446
|
+
* 2. Sum of bits * 2^i = value
|
|
447
|
+
*/
|
|
448
|
+
addBitDecompositionConstraints(valueIdx) {
|
|
449
|
+
const bitIndices = [];
|
|
450
|
+
for (let i = 0; i < this.COMPARISON_BITS; i++) {
|
|
451
|
+
const bitIdx = this.nextWitnessIdx++;
|
|
452
|
+
bitIndices.push(bitIdx);
|
|
453
|
+
this.constraints.push({
|
|
454
|
+
a: [["0x1", bitIdx]],
|
|
455
|
+
b: [["0x1", bitIdx]],
|
|
456
|
+
c: [["0x1", bitIdx]]
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
this.auxWitnessComputations.push({
|
|
460
|
+
type: "bit_decompose",
|
|
461
|
+
targetIdx: bitIndices[0],
|
|
462
|
+
// First bit index (for reference)
|
|
463
|
+
sourceIdx: valueIdx,
|
|
464
|
+
bitIndices,
|
|
465
|
+
numBits: this.COMPARISON_BITS
|
|
466
|
+
});
|
|
467
|
+
const sumTerms = [];
|
|
468
|
+
for (let i = 0; i < this.COMPARISON_BITS; i++) {
|
|
469
|
+
const coeff = (1n << BigInt(i)).toString(16);
|
|
470
|
+
sumTerms.push([`0x${coeff}`, bitIndices[i]]);
|
|
471
|
+
}
|
|
472
|
+
this.constraints.push({
|
|
473
|
+
a: sumTerms,
|
|
474
|
+
b: [["0x1", 0]],
|
|
475
|
+
// * 1
|
|
476
|
+
c: [["0x1", valueIdx]]
|
|
477
|
+
});
|
|
478
|
+
}
|
|
371
479
|
/**
|
|
372
480
|
* Process a variable declaration: let x = expr
|
|
373
481
|
* Creates a new witness for x and adds constraint if needed
|
|
@@ -789,6 +897,15 @@ authors = [""]
|
|
|
789
897
|
const strVal = String(value);
|
|
790
898
|
witnessMap[r1csIndex.toString()] = strVal;
|
|
791
899
|
}
|
|
900
|
+
const r1cs = JSON.parse(circuit.r1csJson);
|
|
901
|
+
if (r1cs.auxWitnessComputations && r1cs.auxWitnessComputations.length > 0) {
|
|
902
|
+
console.log("=== AUXILIARY WITNESS COMPUTATION ===");
|
|
903
|
+
console.log("Input witnesses:", JSON.stringify(witnessMap, null, 2));
|
|
904
|
+
console.log("Computations:", JSON.stringify(r1cs.auxWitnessComputations, null, 2));
|
|
905
|
+
this.computeAuxiliaryWitnesses(witnessMap, r1cs.auxWitnessComputations);
|
|
906
|
+
console.log("After aux computation:", JSON.stringify(witnessMap, null, 2));
|
|
907
|
+
console.log("=====================================");
|
|
908
|
+
}
|
|
792
909
|
const witnessJson = JSON.stringify(witnessMap);
|
|
793
910
|
let provingKey = circuit.provingKey;
|
|
794
911
|
if (!provingKey) {
|
|
@@ -841,6 +958,63 @@ authors = [""]
|
|
|
841
958
|
publicInputs.length
|
|
842
959
|
);
|
|
843
960
|
}
|
|
961
|
+
/**
|
|
962
|
+
* Compute auxiliary witnesses based on computation instructions from R1csBuilder.
|
|
963
|
+
* This handles witnesses that noir_js cannot compute (e.g., bit decomposition for >= operator).
|
|
964
|
+
*/
|
|
965
|
+
computeAuxiliaryWitnesses(witnessMap, computations) {
|
|
966
|
+
const FR_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001");
|
|
967
|
+
const getWitnessValue = (idx) => {
|
|
968
|
+
if (idx === 0) return 1n;
|
|
969
|
+
const val = witnessMap[idx.toString()];
|
|
970
|
+
if (val === void 0) {
|
|
971
|
+
throw new Error(`Witness w_${idx} not found`);
|
|
972
|
+
}
|
|
973
|
+
if (val.startsWith("0x")) {
|
|
974
|
+
return BigInt(val);
|
|
975
|
+
}
|
|
976
|
+
return BigInt(val);
|
|
977
|
+
};
|
|
978
|
+
const setWitnessValue = (idx, val) => {
|
|
979
|
+
const normalized = (val % FR_MODULUS + FR_MODULUS) % FR_MODULUS;
|
|
980
|
+
witnessMap[idx.toString()] = "0x" + normalized.toString(16);
|
|
981
|
+
};
|
|
982
|
+
for (const comp of computations) {
|
|
983
|
+
switch (comp.type) {
|
|
984
|
+
case "subtract": {
|
|
985
|
+
const left = getWitnessValue(comp.leftIdx);
|
|
986
|
+
const right = getWitnessValue(comp.rightIdx);
|
|
987
|
+
const offset = BigInt(comp.offset ?? 0);
|
|
988
|
+
const result = left - right + offset;
|
|
989
|
+
setWitnessValue(comp.targetIdx, result);
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
case "bit_decompose": {
|
|
993
|
+
const source = getWitnessValue(comp.sourceIdx);
|
|
994
|
+
const bitIndices = comp.bitIndices;
|
|
995
|
+
const numBits = comp.numBits;
|
|
996
|
+
if (source < 0n) {
|
|
997
|
+
throw new Error(
|
|
998
|
+
`Bit decomposition failed: value ${source} is negative. This means the comparison constraint is not satisfied.`
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
const maxVal = (1n << BigInt(numBits)) - 1n;
|
|
1002
|
+
if (source > maxVal) {
|
|
1003
|
+
throw new Error(
|
|
1004
|
+
`Bit decomposition failed: value ${source} exceeds ${numBits} bits (max: ${maxVal}). Consider using a larger bit width or smaller values.`
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
for (let i = 0; i < numBits; i++) {
|
|
1008
|
+
const bit = source >> BigInt(i) & 1n;
|
|
1009
|
+
setWitnessValue(bitIndices[i], bit);
|
|
1010
|
+
}
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
default:
|
|
1014
|
+
throw new Error(`Unknown auxiliary witness computation type: ${comp.type}`);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
844
1018
|
/**
|
|
845
1019
|
* Get the verifying key in gnark format for on-chain deployment
|
|
846
1020
|
*/
|
|
@@ -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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
*/
|